// ***** This file is automatically generated from Quant.java.jpp and QuantBody.java.jpp.

package daikon;

import java.util.*;
import java.lang.reflect.*;

/**
 * The Quant library provides routines that operate on arrays and
 * collections.
 *
 * These routines are used by the Java family of output
 * formats. This allows invariants to be output as snippets of
 * executable (Java) code. For example, an invariant like
 * <pre>
 *  a[] elements >= 1
 * </pre>
 * is output (in the Java, JML, and DBC formats) as something like
 * <pre>
 *  daikon.Quant.eltsGTE(a, 1)
 * </pre>
 *
 * <h3>Naming</h3>
 *
 * The library methods have names of the following forms, where OP
 * indicates an operation:  Equal, NotEqual, GT (greater than), GTE
 * (greater than or equal to), LT, LTE.
 * <ul>
 * <li>pairwiseOP.  Apply OP to corresponding elements of two arrays:
 *     a[0] OP b[0], a[1] OP b[1], etc.
 * <li>eltsOP.  Apply OP to each element of one array, and a scalar:
 *     a[0] OP x, a[1] OP x, etc.
 * <li>eltwiseOP:  Apply OP to adjacent elements of one array:
 *     a[0] OP a[1], a[1] OP a[2], etc.
 * <li>lexOP:  Determine lexical ordering:
 *     compare two arrays pairwise, stopping as soon as the result is known.
 * <li>eltsOPindex:  Apply op to array elements and their indices:
 *     a[0] OP 0, a[1] OP 1, etc.
 * </ul>
 *
 * <h3>Equality semantics</h3>
 *
 * Whenever a method involves comparing two elements for equality, this is
 * always "==" equality (even for Objects and Strings).
 *
 * <h3>No exceptions thrown</h3>
 *
 * The library strives not to throw exceptions, even if illegal arguments
 * are passed to the routines.  This has two consequences.<p>
 *
 * First, each predicate (boolean method) returns false when invoked on an
 * illegal argument such as a null collection (array or Collection).<p>
 *
 * Second, each accessor method returns a default "bad" value if inovked on
 * an illegal argument.  For example, the default value for the double type
 * is Double.NaN.<p>
 *
 * The rationale for the decision to never throw exceptions is that we wish
 * to be able to invoke the Quant methods at run time without distrubing
 * execution of a program, and without forcing clients to write a try
 * .. catch block around each invocation.<p>
 *
 * A downside of the decision is that if the default value is returned, it
 * may be impossible for a client to determine whether the method really
 * returned that value, or whether the invocation involved an illegal
 * argument.  To avoid this problem, it is generally better to use a Quant
 * library predicate rather than returning a value and then testing it
 * externally.
 */
public final class Quant {
  private Quant() { throw new Error("do not instantiate"); }

  public static utilMDE.FuzzyFloat fuzzy = new utilMDE.FuzzyFloat();

  /** Returns the size of the array or collection.
   * If the argument is null or not an array or collection, returns a
   * default value (Integer.MAX_VALUE).
   * Thus, for an array a, this never throws an exception, though a.length may.
   */
  /* Following spec depends on JDK specs that come with ESC/Java2
   * by Christoph Csallner */  
  // Not called from Quant; provided only for external use.
  /*@ public normal_behavior 
    @   ensures o==null || (!o.getClass().isArray() && !(o instanceof Collection))
    @     	==> \result==Integer.MAX_VALUE;
    @   ensures o.getClass().isArray()
    @				==> \result==Array.getLength(o);
    @   ensures o instanceof Collection
    @				==> \result==((Collection)o).size();
    @*/	
  /*@ pure */ public static int size(Object o) {
    if (o == null) { return Integer.MAX_VALUE; } // return default value
    java.lang.Class c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getLength(o);
    } else if (o instanceof Collection) {
      return ((Collection)o).size();
    } else {
      return Integer.MAX_VALUE; // return default value
    }
  }

  /** Returns the size of the collection.
   * If the argument is null, returns a default value (Integer.MAX_VALUE).
   */
  // Not called from Quant; provided only for external use.
  /* Following spec depends on JDK specs that come with ESC/Java2
   * by Christoph Csallner */  
  /*@ public normal_behavior 
   	@   ensures o==null
   	@     	==> \result==Integer.MAX_VALUE;
    @   ensures o instanceof Collection
    @				==> \result==((Collection)o).size();
    @*/	  
  /*@ pure */ public static int size(Collection o) {
    if (o == null) { return Integer.MAX_VALUE; } // return default value
    return o.size();
  }

  /** True iff the sequence contains no null elements. */
  /*@ pure */ public static boolean eltsNonNull(Object[] seq1) {
    if (seq1 == null) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (seq1[i] == null) {
        return false;
      }
    }
    return true;
  }

  ///////////////////////////////////////////////////////////////////////////
  /// Methods for "boolean" (from QuantBody.java.jpp)
  ///

  /** Returns the ith element of the array or collection argument.
   * If the argument is null or not an array or collection, returns a
   * default value (false).
   **/
  /*@ pure */ public static boolean getElement_boolean (Object o, long i) {
    if (o == null) { return false; } // return default value
    java.lang.Class c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getBoolean (o, (int)i);
    } else if (o instanceof java.util.AbstractCollection) {
      return java.lang.reflect.Array.getBoolean (((java.util.AbstractCollection)o).toArray(), (int)i);
    } else {
      return false; // return default value
    }
  }

  /*@ pure */ public static boolean getElement_boolean (boolean[] arr, long i) {
    if (arr == null) { return false; } // return default value
    return arr[(int)i];
  }

  private static boolean eq(boolean x, boolean y) {
    return (x == y);
  }

  private static boolean ne(boolean x, boolean y) {
    return x != y;
  }

  /** True iff both sequences are non-null and have the same length. */
  /*@ pure */ public static boolean sameLength(boolean[] seq1, boolean[] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] }
   * If either array is null, returns null.
   * If either array is empty, returns only those elements in the other array.
   * If both arrays are empty, returns a new empty array.
   */
  /*@ pure */ public static boolean[] concat(boolean[] seq1, boolean[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return utilMDE.ArraysMDE.concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static boolean[] union(boolean[] seq1, boolean[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static boolean[] intersection(boolean[] seq1, boolean[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    boolean[] intermediate = new boolean[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static boolean[] setDiff(boolean[] seq1, boolean[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    boolean[] intermediate = new boolean[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Retuns true iff seq1 and seq2 are equal when considered as sets.
   */
  /*@ pure */ public static boolean setEqual(boolean[] seq1, boolean[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean isReverse(boolean[] seq1, boolean[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /**
   * True iff seq1 is a subset of seq2, when the sequences are
   * considered as sets.
   */
  /*@ pure */ public static boolean subsetOf(boolean[] seq1, boolean[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq contains no duplicate elements.
   */
  /*@ pure */ public static boolean noDups(boolean[] seq) {
    if (seq == null) { return false; }
    return utilMDE.ArraysMDE.noDuplicates(seq);
  }

 /**
  * Returns true iff elt is in array arr.
  */
  /*@ pure */ public static boolean memberOf(boolean elt, boolean[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /**
   * Returns a subsequence of seq with first elements seq[start] and
   * last element seq[end].
   */
  /*@ pure */ public static boolean[] slice(boolean[] seq, int start, int end) {
    if (seq == null) { return null; }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) { return new boolean[] { }; }
    if (end > seq.length-1) { return new boolean[] { }; }
    if (sliceStart > sliceEnd) { return new boolean[] { }; }
    int length = sliceEnd - sliceStart + 1;
    return utilMDE.ArraysMDE.subarray(seq, sliceStart, length);
  }

  /*@ pure */ public static boolean[] slice(boolean[] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  /*@ pure */ public static boolean[] slice(boolean[] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  /*@ pure */ public static boolean[] slice(boolean[] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  /*@ pure */ public static boolean eltsEqual(boolean[] arr, boolean elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  /*@ pure */ public static boolean eltsNotEqual(boolean[] arr, boolean elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  /*@ pure */ public static boolean pairwiseEqual(boolean[] seq1, boolean[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseNotEqual(boolean[] seq1, boolean[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  /*@ pure */ public static boolean lexEqual(boolean[] seq1, boolean[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, seq2);
  }

  /**
   * Returns true iff seq1 is lexically not equal to seq2.
   */
  /*@ pure */ public static boolean lexNotEqual(boolean[] seq1, boolean[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return !lexEqual(seq1, seq2);
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseEqual(boolean[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (ne(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseNotEqual(boolean[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (eq(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /// Deferencing (accessing) fields

  /**
   * collectboolean accepts an object and a list of fields (one of which is
   * of array type, and the rest of which are not), and produces an array in
   * which the original object has had the given fields accessed.
   *
   * Daikon creates invariants over "variables" such as the following.
   *
   * x.arr[].z   The result of collecting all elements y.z for all y's
   *             in array x.arr.
   * arr[].y.z   The result of collecting all elements x.y.z for all x's
   *             in array arr.
   * x.y.z[]     The result of collecting all elements in array x.y.z[]
   *
   * The collectboolean() method does this collecting work.
   *
   * Given an object (x, arr, or x, correspondingly, in the above examples)
   * and a "field string" (arr.z, y.z, or y.z, correspondingly, in the
   * above example), the collect method collects the elements the result
   * from following the fields, one of which is assumed to be an array.
   *
   * requires: fieldStr.length() > 0 and object != null
   * requires: fieldStr contains only field names, no "[]" strings.
   *
   * requires: the method only works for field sequences with exactly
   * one field representing an array. For example, the collection
   * a[].b[].c will fail.
   *
   * If the resulting collection is of non-primitive type, then collect
   * returns an array of type Object[].
   *
   * Returns null if any array or field access causes an exception.
   */
  /*@ pure */ public static boolean[] collectboolean (Object object, String fieldStr) {

    if (object == null) { return null; }
    if (fieldStr == null) { return null; }

    //Assert.assertTrue(fieldStr != null && !"".equals(fieldStr));
    String[] fieldNames = fieldStr.split("\\.");
    boolean[] retval = collectboolean (object, fieldNames, 0);
    //System.err.println("%%% fieldArray returned: " + utilMDE.ArraysMDE.toString(retval));
    return retval;
  }

  /** Helper method for collectboolean(Object, String).
   * Operates on the fields specified in fields[fieldsStartIdx..].
   * @see collectboolean(Object, String)
   */
  /*@ pure */ private static boolean[] collectboolean (Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) { return null; }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj = null;
    try {
      Field field = (object instanceof java.lang.Class)
        ? ((Class)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      //System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (boolean[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectboolean_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        //Assert.assertTrue(cls.equals(Boolean.TYPE));
        return new boolean[] { ((Boolean)fieldObj).booleanValue() };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection collection = (java.util.AbstractCollection)fieldObj;
        boolean[] intermediate = new boolean[collection.size()];
        int index = 0;
        for (Iterator i = collection.iterator() ; i.hasNext() ; ) {
          boolean[] oneEltArray = collectboolean (i.next(), fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        boolean[] intermediate = new boolean[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          boolean[] oneEltArray = collectboolean (Array.get(fieldObj, i),
                                             fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectboolean (fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  // Returns the results of dereferencing the fields specified in
  // fields[fieldsStartIdx..] for 'object'.
  // Returns a default value if any field access causes an exception.
  /*@ pure */ public static boolean collectboolean_field (Object object, String fieldStr) {

    if (object == null) { return false; } // return default value
    if (fieldStr == null) { return false; } // return default value

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class)
          ? ((Class)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

      } catch (Exception e) {
        return false; // return default value

      }

    }

    return ((Boolean)fieldObj).booleanValue();
  }

  ///////////////////////////////////////////////////////////////////////////
  /// Methods for "byte" (from QuantBody.java.jpp)
  ///

  /** Returns the ith element of the array or collection argument.
   * If the argument is null or not an array or collection, returns a
   * default value (Byte.MAX_VALUE).
   **/
  /*@ pure */ public static byte getElement_byte (Object o, long i) {
    if (o == null) { return Byte.MAX_VALUE; } // return default value
    java.lang.Class c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getByte (o, (int)i);
    } else if (o instanceof java.util.AbstractCollection) {
      return java.lang.reflect.Array.getByte (((java.util.AbstractCollection)o).toArray(), (int)i);
    } else {
      return Byte.MAX_VALUE; // return default value
    }
  }

  /*@ pure */ public static byte getElement_byte (byte[] arr, long i) {
    if (arr == null) { return Byte.MAX_VALUE; } // return default value
    return arr[(int)i];
  }

  private static boolean eq(byte x, byte y) {
    return (x == y);
  }

  private static boolean ne(byte x, byte y) {
    return x != y;
  }

  private static boolean lt(byte x, byte y) {
    return x < y;
  }

  private static boolean lte(byte x, byte y) {
    return x <= y;
  }

  private static boolean gt(byte x, byte y) {
    return x > y;
  }

  private static boolean gte(byte x, byte y) {
    return x >= y;
  }

  /** True iff both sequences are non-null and have the same length. */
  /*@ pure */ public static boolean sameLength(byte[] seq1, byte[] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences are non-null and have the same length. */
  /*@ pure */ public static boolean sameLength(byte[] seq1, int[] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences have the same length, and all seq2[i] divide seq1[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq2[i] divides seq1[i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean pairwiseDivides(byte[] seq1, byte[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }
  /*@ pure */ public static boolean pairwiseDivides(byte[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }

  /** True iff both sequences have the same length, and all seq1[i] ==  seq2[i] * seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] ==  seq2[i] * seq2[i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean pairwiseSquare(byte[] seq1, byte[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i] * seq2[i])) {
        return false;
      }
    }
    return true;
  }
  /*@ pure */ public static boolean pairwiseSquare(byte[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i] * seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] }
   * If either array is null, returns null.
   * If either array is empty, returns only those elements in the other array.
   * If both arrays are empty, returns a new empty array.
   */
  /*@ pure */ public static byte[] concat(byte[] seq1, byte[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return utilMDE.ArraysMDE.concat(seq1, seq2);
  }

  /*@ pure */ public static int[] concat(byte[] seq1, int[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    // Cannot just use utilMDE.ArraysMDE.concat because the two arrays
    // have different types.  This essentially inlines that method.
    int newLength = seq1.length + seq2.length;
    int[] retval = new int[newLength];
    System.arraycopy(seq1, 0, retval, 0, seq1.length);
    for (int j = 0 ; j < seq2.length ; j++) {
      retval[seq1.length+j] = seq2[j];
    }
    return retval;
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static byte[] union(byte[] seq1, byte[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return concat(seq1, seq2);
  }

  /*@ pure */ public static int[] union(byte[] seq1, int[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static byte[] intersection(byte[] seq1, byte[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    byte[] intermediate = new byte[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /*@ pure */ public static int[] intersection(byte[] seq1, int[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    int[] intermediate = new int[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static byte[] setDiff(byte[] seq1, byte[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    byte[] intermediate = new byte[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /*@ pure */ public static int[] setDiff(byte[] seq1, int[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    int[] intermediate = new int[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Retuns true iff seq1 and seq2 are equal when considered as sets.
   */
  /*@ pure */ public static boolean setEqual(byte[] seq1, byte[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean setEqual(byte[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean isReverse(byte[] seq1, byte[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean isReverse(byte[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /**
   * True iff seq1 is a subset of seq2, when the sequences are
   * considered as sets.
   */
  /*@ pure */ public static boolean subsetOf(byte[] seq1, byte[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean subsetOf(byte[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq contains no duplicate elements.
   */
  /*@ pure */ public static boolean noDups(byte[] seq) {
    if (seq == null) { return false; }
    return utilMDE.ArraysMDE.noDuplicates(seq);
  }

 /**
  * Returns true iff elt is in array arr.
  */
  /*@ pure */ public static boolean memberOf(byte elt, byte[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /*@ pure */ public static boolean memberOf(byte elt, int[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /*@ pure */ public static boolean memberOf(long elt, byte[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /**
   * Returns a subsequence of seq with first elements seq[start] and
   * last element seq[end].
   */
  /*@ pure */ public static byte[] slice(byte[] seq, int start, int end) {
    if (seq == null) { return null; }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) { return new byte[] { }; }
    if (end > seq.length-1) { return new byte[] { }; }
    if (sliceStart > sliceEnd) { return new byte[] { }; }
    int length = sliceEnd - sliceStart + 1;
    return utilMDE.ArraysMDE.subarray(seq, sliceStart, length);
  }

  /*@ pure */ public static byte[] slice(byte[] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  /*@ pure */ public static byte[] slice(byte[] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  /*@ pure */ public static byte[] slice(byte[] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  /*@ pure */ public static boolean eltsEqual(byte[] arr, byte elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsEqual(byte[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  /*@ pure */ public static boolean eltsNotEqual(byte[] arr, byte elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsNotEqual(byte[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is greater than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] > elt
   *
   */
  /*@ pure */ public static boolean eltsGT(byte[] arr, byte elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsGT(byte[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is greater than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] >= elt
   *
   */
  /*@ pure */ public static boolean eltsGTE(byte[] arr, byte elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsGTE(byte[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is less than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] < elt
   *
   */
  /*@ pure */ public static boolean eltsLT(byte[] arr, byte elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsLT(byte[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is less than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] <= elt
   *
   */
  /*@ pure */ public static boolean eltsLTE(byte[] arr, byte elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsLTE(byte[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  /*@ pure */ public static boolean pairwiseEqual(byte[] seq1, byte[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseEqual(byte[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseNotEqual(byte[] seq1, byte[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseNotEqual(byte[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] < seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] < seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseLT(byte[] seq1, byte[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseLT(byte[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] <= seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] <= seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseLTE(byte[] seq1, byte[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseLTE(byte[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] > seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] > seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseGT(byte[] seq1, byte[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseGT(byte[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] >= seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] >= seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseGTE(byte[] seq1, byte[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseGTE(byte[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  /*@ pure */ public static boolean lexEqual(byte[] seq1, byte[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, seq2);
  }

  /*@ pure */ public static boolean lexEqual(byte[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, seq2);
  }

  /**
   * Returns true iff seq1 is lexically not equal to seq2.
   */
  /*@ pure */ public static boolean lexNotEqual(byte[] seq1, byte[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return !lexEqual(seq1, seq2);
  }

  /*@ pure */ public static boolean lexNotEqual(byte[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return !lexEqual(seq1, seq2);
  }

  /**
   * Returns true iff seq1 is lexically <  seq2.
   */
  /*@ pure */ public static boolean lexLT(byte[] seq1, byte[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length >= seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexLT(byte[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
        } else if (lt(seq1[i], seq2[i])) {
          return true;
        }
      }
      if (seq1.length >= seq2.length) {
        return false;
      }
      return true;
    }

  /**
   * Returns true iff seq1 is lexically <= to seq2.
   */
  /*@ pure */ public static boolean lexLTE(byte[] seq1, byte[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexLTE(byte[] seq1, int[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically > to seq2.
   */
  /*@ pure */ public static boolean lexGT(byte[] seq1, byte[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexGT(byte[] seq1, int[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically >= to seq2.
   */
  /*@ pure */ public static boolean lexGTE(byte[] seq1, byte[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexGTE(byte[] seq1, int[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseEqual(byte[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (ne(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseNotEqual(byte[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (eq(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] < seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] < seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseLT(byte[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (gte(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] <= seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] <= seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseLTE(byte[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (gt(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] > seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] > seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseGT(byte[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (lte(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] >= seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] >= seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseGTE(byte[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (lt(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] == i
   *
   */
  /*@ pure */ public static boolean eltsEqualIndex(byte[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (ne(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] != i
   *
   */
  /*@ pure */ public static boolean eltsNotEqualIndex(byte[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (eq(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] < i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] < i
   *
   */
  /*@ pure */ public static boolean eltsLtIndex(byte[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] <= i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] <= i
   *
   */
  /*@ pure */ public static boolean eltsLteIndex(byte[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] > i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] > i
   *
   */
  /*@ pure */ public static boolean  eltsGtIndex(byte[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] >= i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] >= i
   *
   */
  /*@ pure */ public static boolean eltsGteIndex(byte[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /// Deferencing (accessing) fields

  /**
   * collectbyte accepts an object and a list of fields (one of which is
   * of array type, and the rest of which are not), and produces an array in
   * which the original object has had the given fields accessed.
   *
   * Daikon creates invariants over "variables" such as the following.
   *
   * x.arr[].z   The result of collecting all elements y.z for all y's
   *             in array x.arr.
   * arr[].y.z   The result of collecting all elements x.y.z for all x's
   *             in array arr.
   * x.y.z[]     The result of collecting all elements in array x.y.z[]
   *
   * The collectbyte() method does this collecting work.
   *
   * Given an object (x, arr, or x, correspondingly, in the above examples)
   * and a "field string" (arr.z, y.z, or y.z, correspondingly, in the
   * above example), the collect method collects the elements the result
   * from following the fields, one of which is assumed to be an array.
   *
   * requires: fieldStr.length() > 0 and object != null
   * requires: fieldStr contains only field names, no "[]" strings.
   *
   * requires: the method only works for field sequences with exactly
   * one field representing an array. For example, the collection
   * a[].b[].c will fail.
   *
   * If the resulting collection is of non-primitive type, then collect
   * returns an array of type Object[].
   *
   * Returns null if any array or field access causes an exception.
   */
  /*@ pure */ public static byte[] collectbyte (Object object, String fieldStr) {

    if (object == null) { return null; }
    if (fieldStr == null) { return null; }

    //Assert.assertTrue(fieldStr != null && !"".equals(fieldStr));
    String[] fieldNames = fieldStr.split("\\.");
    byte[] retval = collectbyte (object, fieldNames, 0);
    //System.err.println("%%% fieldArray returned: " + utilMDE.ArraysMDE.toString(retval));
    return retval;
  }

  /** Helper method for collectbyte(Object, String).
   * Operates on the fields specified in fields[fieldsStartIdx..].
   * @see collectbyte(Object, String)
   */
  /*@ pure */ private static byte[] collectbyte (Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) { return null; }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj = null;
    try {
      Field field = (object instanceof java.lang.Class)
        ? ((Class)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      //System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (byte[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectbyte_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        //Assert.assertTrue(cls.equals(Byte.TYPE));
        return new byte[] { ((Byte)fieldObj).byteValue() };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection collection = (java.util.AbstractCollection)fieldObj;
        byte[] intermediate = new byte[collection.size()];
        int index = 0;
        for (Iterator i = collection.iterator() ; i.hasNext() ; ) {
          byte[] oneEltArray = collectbyte (i.next(), fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        byte[] intermediate = new byte[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          byte[] oneEltArray = collectbyte (Array.get(fieldObj, i),
                                             fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectbyte (fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  // Returns the results of dereferencing the fields specified in
  // fields[fieldsStartIdx..] for 'object'.
  // Returns a default value if any field access causes an exception.
  /*@ pure */ public static byte collectbyte_field (Object object, String fieldStr) {

    if (object == null) { return Byte.MAX_VALUE; } // return default value
    if (fieldStr == null) { return Byte.MAX_VALUE; } // return default value

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class)
          ? ((Class)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

      } catch (Exception e) {
        return Byte.MAX_VALUE; // return default value

      }

    }

    return ((Byte)fieldObj).byteValue();
  }

  ///////////////////////////////////////////////////////////////////////////
  /// Methods for "char" (from QuantBody.java.jpp)
  ///

  /** Returns the ith element of the array or collection argument.
   * If the argument is null or not an array or collection, returns a
   * default value (Character.MAX_VALUE).
   **/
  /*@ pure */ public static char getElement_char (Object o, long i) {
    if (o == null) { return Character.MAX_VALUE; } // return default value
    java.lang.Class c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getChar (o, (int)i);
    } else if (o instanceof java.util.AbstractCollection) {
      return java.lang.reflect.Array.getChar (((java.util.AbstractCollection)o).toArray(), (int)i);
    } else {
      return Character.MAX_VALUE; // return default value
    }
  }

  /*@ pure */ public static char getElement_char (char[] arr, long i) {
    if (arr == null) { return Character.MAX_VALUE; } // return default value
    return arr[(int)i];
  }

  private static boolean eq(char x, char y) {
    return (x == y);
  }

  private static boolean ne(char x, char y) {
    return x != y;
  }

  private static boolean lt(char x, char y) {
    return x < y;
  }

  private static boolean lte(char x, char y) {
    return x <= y;
  }

  private static boolean gt(char x, char y) {
    return x > y;
  }

  private static boolean gte(char x, char y) {
    return x >= y;
  }

  /** True iff both sequences are non-null and have the same length. */
  /*@ pure */ public static boolean sameLength(char[] seq1, char[] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] }
   * If either array is null, returns null.
   * If either array is empty, returns only those elements in the other array.
   * If both arrays are empty, returns a new empty array.
   */
  /*@ pure */ public static char[] concat(char[] seq1, char[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return utilMDE.ArraysMDE.concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static char[] union(char[] seq1, char[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static char[] intersection(char[] seq1, char[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    char[] intermediate = new char[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static char[] setDiff(char[] seq1, char[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    char[] intermediate = new char[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Retuns true iff seq1 and seq2 are equal when considered as sets.
   */
  /*@ pure */ public static boolean setEqual(char[] seq1, char[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean isReverse(char[] seq1, char[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /**
   * True iff seq1 is a subset of seq2, when the sequences are
   * considered as sets.
   */
  /*@ pure */ public static boolean subsetOf(char[] seq1, char[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq contains no duplicate elements.
   */
  /*@ pure */ public static boolean noDups(char[] seq) {
    if (seq == null) { return false; }
    return utilMDE.ArraysMDE.noDuplicates(seq);
  }

 /**
  * Returns true iff elt is in array arr.
  */
  /*@ pure */ public static boolean memberOf(char elt, char[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /**
   * Returns a subsequence of seq with first elements seq[start] and
   * last element seq[end].
   */
  /*@ pure */ public static char[] slice(char[] seq, int start, int end) {
    if (seq == null) { return null; }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) { return new char[] { }; }
    if (end > seq.length-1) { return new char[] { }; }
    if (sliceStart > sliceEnd) { return new char[] { }; }
    int length = sliceEnd - sliceStart + 1;
    return utilMDE.ArraysMDE.subarray(seq, sliceStart, length);
  }

  /*@ pure */ public static char[] slice(char[] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  /*@ pure */ public static char[] slice(char[] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  /*@ pure */ public static char[] slice(char[] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  /*@ pure */ public static boolean eltsEqual(char[] arr, char elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  /*@ pure */ public static boolean eltsNotEqual(char[] arr, char elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  /*@ pure */ public static boolean pairwiseEqual(char[] seq1, char[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseNotEqual(char[] seq1, char[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  /*@ pure */ public static boolean lexEqual(char[] seq1, char[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, seq2);
  }

  /**
   * Returns true iff seq1 is lexically not equal to seq2.
   */
  /*@ pure */ public static boolean lexNotEqual(char[] seq1, char[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return !lexEqual(seq1, seq2);
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseEqual(char[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (ne(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseNotEqual(char[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (eq(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /// Deferencing (accessing) fields

  /**
   * collectchar accepts an object and a list of fields (one of which is
   * of array type, and the rest of which are not), and produces an array in
   * which the original object has had the given fields accessed.
   *
   * Daikon creates invariants over "variables" such as the following.
   *
   * x.arr[].z   The result of collecting all elements y.z for all y's
   *             in array x.arr.
   * arr[].y.z   The result of collecting all elements x.y.z for all x's
   *             in array arr.
   * x.y.z[]     The result of collecting all elements in array x.y.z[]
   *
   * The collectchar() method does this collecting work.
   *
   * Given an object (x, arr, or x, correspondingly, in the above examples)
   * and a "field string" (arr.z, y.z, or y.z, correspondingly, in the
   * above example), the collect method collects the elements the result
   * from following the fields, one of which is assumed to be an array.
   *
   * requires: fieldStr.length() > 0 and object != null
   * requires: fieldStr contains only field names, no "[]" strings.
   *
   * requires: the method only works for field sequences with exactly
   * one field representing an array. For example, the collection
   * a[].b[].c will fail.
   *
   * If the resulting collection is of non-primitive type, then collect
   * returns an array of type Object[].
   *
   * Returns null if any array or field access causes an exception.
   */
  /*@ pure */ public static char[] collectchar (Object object, String fieldStr) {

    if (object == null) { return null; }
    if (fieldStr == null) { return null; }

    //Assert.assertTrue(fieldStr != null && !"".equals(fieldStr));
    String[] fieldNames = fieldStr.split("\\.");
    char[] retval = collectchar (object, fieldNames, 0);
    //System.err.println("%%% fieldArray returned: " + utilMDE.ArraysMDE.toString(retval));
    return retval;
  }

  /** Helper method for collectchar(Object, String).
   * Operates on the fields specified in fields[fieldsStartIdx..].
   * @see collectchar(Object, String)
   */
  /*@ pure */ private static char[] collectchar (Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) { return null; }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj = null;
    try {
      Field field = (object instanceof java.lang.Class)
        ? ((Class)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      //System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (char[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectchar_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        //Assert.assertTrue(cls.equals(Character.TYPE));
        return new char[] { ((Character)fieldObj).charValue() };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection collection = (java.util.AbstractCollection)fieldObj;
        char[] intermediate = new char[collection.size()];
        int index = 0;
        for (Iterator i = collection.iterator() ; i.hasNext() ; ) {
          char[] oneEltArray = collectchar (i.next(), fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        char[] intermediate = new char[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          char[] oneEltArray = collectchar (Array.get(fieldObj, i),
                                             fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectchar (fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  // Returns the results of dereferencing the fields specified in
  // fields[fieldsStartIdx..] for 'object'.
  // Returns a default value if any field access causes an exception.
  /*@ pure */ public static char collectchar_field (Object object, String fieldStr) {

    if (object == null) { return Character.MAX_VALUE; } // return default value
    if (fieldStr == null) { return Character.MAX_VALUE; } // return default value

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class)
          ? ((Class)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

      } catch (Exception e) {
        return Character.MAX_VALUE; // return default value

      }

    }

    return ((Character)fieldObj).charValue();
  }

  ///////////////////////////////////////////////////////////////////////////
  /// Methods for "double" (from QuantBody.java.jpp)
  ///

  /** Returns the ith element of the array or collection argument.
   * If the argument is null or not an array or collection, returns a
   * default value (Double.NaN).
   **/
  /*@ pure */ public static double getElement_double (Object o, long i) {
    if (o == null) { return Double.NaN; } // return default value
    java.lang.Class c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getDouble (o, (int)i);
    } else if (o instanceof java.util.AbstractCollection) {
      return java.lang.reflect.Array.getDouble (((java.util.AbstractCollection)o).toArray(), (int)i);
    } else {
      return Double.NaN; // return default value
    }
  }

  /*@ pure */ public static double getElement_double (double[] arr, long i) {
    if (arr == null) { return Double.NaN; } // return default value
    return arr[(int)i];
  }

  private static boolean eq(double x, double y) {
    return fuzzy.eq(x,y);
  }

  private static boolean ne(double x, double y) {
    return fuzzy.ne(x,y);
  }

  private static boolean lt(double x, double y) {
    return fuzzy.lt(x,y);
  }

  private static boolean lte(double x, double y) {
    return fuzzy.lte(x,y);
  }

  private static boolean gt(double x, double y) {
    return fuzzy.gt(x,y);
  }

  private static boolean gte(double x, double y) {
    return fuzzy.gte(x,y);
  }

  /** True iff both sequences are non-null and have the same length. */
  /*@ pure */ public static boolean sameLength(double[] seq1, double[] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences are non-null and have the same length. */
  /*@ pure */ public static boolean sameLength(double[] seq1, float[] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences have the same length, and all seq2[i] divide seq1[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq2[i] divides seq1[i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean pairwiseDivides(double[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }
  /*@ pure */ public static boolean pairwiseDivides(double[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }

  /** True iff both sequences have the same length, and all seq1[i] ==  seq2[i] * seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] ==  seq2[i] * seq2[i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean pairwiseSquare(double[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i] * seq2[i])) {
        return false;
      }
    }
    return true;
  }
  /*@ pure */ public static boolean pairwiseSquare(double[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i] * seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] }
   * If either array is null, returns null.
   * If either array is empty, returns only those elements in the other array.
   * If both arrays are empty, returns a new empty array.
   */
  /*@ pure */ public static double[] concat(double[] seq1, double[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return utilMDE.ArraysMDE.concat(seq1, seq2);
  }

  /*@ pure */ public static double[] concat(double[] seq1, float[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    // Cannot just use utilMDE.ArraysMDE.concat because the two arrays
    // have different types.  This essentially inlines that method.
    int newLength = seq1.length + seq2.length;
    double[] retval = new double[newLength];
    System.arraycopy(seq1, 0, retval, 0, seq1.length);
    for (int j = 0 ; j < seq2.length ; j++) {
      retval[seq1.length+j] = seq2[j];
    }
    return retval;
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static double[] union(double[] seq1, double[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return concat(seq1, seq2);
  }

  /*@ pure */ public static double[] union(double[] seq1, float[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static double[] intersection(double[] seq1, double[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    double[] intermediate = new double[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /*@ pure */ public static double[] intersection(double[] seq1, float[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    double[] intermediate = new double[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static double[] setDiff(double[] seq1, double[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    double[] intermediate = new double[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /*@ pure */ public static double[] setDiff(double[] seq1, float[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    double[] intermediate = new double[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Retuns true iff seq1 and seq2 are equal when considered as sets.
   */
  /*@ pure */ public static boolean setEqual(double[] seq1, double[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean setEqual(double[] seq1, float[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean isReverse(double[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean isReverse(double[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /**
   * True iff seq1 is a subset of seq2, when the sequences are
   * considered as sets.
   */
  /*@ pure */ public static boolean subsetOf(double[] seq1, double[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean subsetOf(double[] seq1, float[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq contains no duplicate elements.
   */
  /*@ pure */ public static boolean noDups(double[] seq) {
    if (seq == null) { return false; }
    return utilMDE.ArraysMDE.noDuplicates(seq);
  }

 /**
  * Returns true iff elt is in array arr.
  */
  /*@ pure */ public static boolean memberOf(double elt, double[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /*@ pure */ public static boolean memberOf(double elt, float[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /**
   * Returns a subsequence of seq with first elements seq[start] and
   * last element seq[end].
   */
  /*@ pure */ public static double[] slice(double[] seq, int start, int end) {
    if (seq == null) { return null; }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) { return new double[] { }; }
    if (end > seq.length-1) { return new double[] { }; }
    if (sliceStart > sliceEnd) { return new double[] { }; }
    int length = sliceEnd - sliceStart + 1;
    return utilMDE.ArraysMDE.subarray(seq, sliceStart, length);
  }

  /*@ pure */ public static double[] slice(double[] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  /*@ pure */ public static double[] slice(double[] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  /*@ pure */ public static double[] slice(double[] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  /*@ pure */ public static boolean eltsEqual(double[] arr, double elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsEqual(double[] arr, float elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  /*@ pure */ public static boolean eltsNotEqual(double[] arr, double elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsNotEqual(double[] arr, float elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is greater than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] > elt
   *
   */
  /*@ pure */ public static boolean eltsGT(double[] arr, double elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsGT(double[] arr, float elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is greater than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] >= elt
   *
   */
  /*@ pure */ public static boolean eltsGTE(double[] arr, double elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsGTE(double[] arr, float elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is less than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] < elt
   *
   */
  /*@ pure */ public static boolean eltsLT(double[] arr, double elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsLT(double[] arr, float elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is less than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] <= elt
   *
   */
  /*@ pure */ public static boolean eltsLTE(double[] arr, double elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsLTE(double[] arr, float elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  /*@ pure */ public static boolean pairwiseEqual(double[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (Double.isNaN(seq1[i]) && Double.isNaN(seq2[i])) {
        continue;
      }
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseEqual(double[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseNotEqual(double[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseNotEqual(double[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] < seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] < seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseLT(double[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseLT(double[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] <= seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] <= seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseLTE(double[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseLTE(double[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] > seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] > seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseGT(double[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseGT(double[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] >= seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] >= seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseGTE(double[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseGTE(double[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  /*@ pure */ public static boolean lexEqual(double[] seq1, double[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, seq2);
  }

  /*@ pure */ public static boolean lexEqual(double[] seq1, float[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, seq2);
  }

  /**
   * Returns true iff seq1 is lexically not equal to seq2.
   */
  /*@ pure */ public static boolean lexNotEqual(double[] seq1, double[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return !lexEqual(seq1, seq2);
  }

  /*@ pure */ public static boolean lexNotEqual(double[] seq1, float[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return !lexEqual(seq1, seq2);
  }

  /**
   * Returns true iff seq1 is lexically <  seq2.
   */
  /*@ pure */ public static boolean lexLT(double[] seq1, double[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length >= seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexLT(double[] seq1, float[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
        } else if (lt(seq1[i], seq2[i])) {
          return true;
        }
      }
      if (seq1.length >= seq2.length) {
        return false;
      }
      return true;
    }

  /**
   * Returns true iff seq1 is lexically <= to seq2.
   */
  /*@ pure */ public static boolean lexLTE(double[] seq1, double[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexLTE(double[] seq1, float[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically > to seq2.
   */
  /*@ pure */ public static boolean lexGT(double[] seq1, double[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexGT(double[] seq1, float[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically >= to seq2.
   */
  /*@ pure */ public static boolean lexGTE(double[] seq1, double[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexGTE(double[] seq1, float[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseEqual(double[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (ne(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseNotEqual(double[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (eq(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] < seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] < seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseLT(double[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (gte(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] <= seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] <= seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseLTE(double[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (gt(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] > seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] > seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseGT(double[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (lte(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] >= seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] >= seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseGTE(double[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (lt(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] == i
   *
   */
  /*@ pure */ public static boolean eltsEqualIndex(double[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (ne(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] != i
   *
   */
  /*@ pure */ public static boolean eltsNotEqualIndex(double[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (eq(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] < i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] < i
   *
   */
  /*@ pure */ public static boolean eltsLtIndex(double[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] <= i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] <= i
   *
   */
  /*@ pure */ public static boolean eltsLteIndex(double[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] > i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] > i
   *
   */
  /*@ pure */ public static boolean  eltsGtIndex(double[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] >= i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] >= i
   *
   */
  /*@ pure */ public static boolean eltsGteIndex(double[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /// Deferencing (accessing) fields

  /**
   * collectdouble accepts an object and a list of fields (one of which is
   * of array type, and the rest of which are not), and produces an array in
   * which the original object has had the given fields accessed.
   *
   * Daikon creates invariants over "variables" such as the following.
   *
   * x.arr[].z   The result of collecting all elements y.z for all y's
   *             in array x.arr.
   * arr[].y.z   The result of collecting all elements x.y.z for all x's
   *             in array arr.
   * x.y.z[]     The result of collecting all elements in array x.y.z[]
   *
   * The collectdouble() method does this collecting work.
   *
   * Given an object (x, arr, or x, correspondingly, in the above examples)
   * and a "field string" (arr.z, y.z, or y.z, correspondingly, in the
   * above example), the collect method collects the elements the result
   * from following the fields, one of which is assumed to be an array.
   *
   * requires: fieldStr.length() > 0 and object != null
   * requires: fieldStr contains only field names, no "[]" strings.
   *
   * requires: the method only works for field sequences with exactly
   * one field representing an array. For example, the collection
   * a[].b[].c will fail.
   *
   * If the resulting collection is of non-primitive type, then collect
   * returns an array of type Object[].
   *
   * Returns null if any array or field access causes an exception.
   */
  /*@ pure */ public static double[] collectdouble (Object object, String fieldStr) {

    if (object == null) { return null; }
    if (fieldStr == null) { return null; }

    //Assert.assertTrue(fieldStr != null && !"".equals(fieldStr));
    String[] fieldNames = fieldStr.split("\\.");
    double[] retval = collectdouble (object, fieldNames, 0);
    //System.err.println("%%% fieldArray returned: " + utilMDE.ArraysMDE.toString(retval));
    return retval;
  }

  /** Helper method for collectdouble(Object, String).
   * Operates on the fields specified in fields[fieldsStartIdx..].
   * @see collectdouble(Object, String)
   */
  /*@ pure */ private static double[] collectdouble (Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) { return null; }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj = null;
    try {
      Field field = (object instanceof java.lang.Class)
        ? ((Class)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      //System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (double[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectdouble_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        //Assert.assertTrue(cls.equals(Double.TYPE));
        return new double[] { ((Double)fieldObj).doubleValue() };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection collection = (java.util.AbstractCollection)fieldObj;
        double[] intermediate = new double[collection.size()];
        int index = 0;
        for (Iterator i = collection.iterator() ; i.hasNext() ; ) {
          double[] oneEltArray = collectdouble (i.next(), fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        double[] intermediate = new double[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          double[] oneEltArray = collectdouble (Array.get(fieldObj, i),
                                             fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectdouble (fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  // Returns the results of dereferencing the fields specified in
  // fields[fieldsStartIdx..] for 'object'.
  // Returns a default value if any field access causes an exception.
  /*@ pure */ public static double collectdouble_field (Object object, String fieldStr) {

    if (object == null) { return Double.NaN; } // return default value
    if (fieldStr == null) { return Double.NaN; } // return default value

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class)
          ? ((Class)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

      } catch (Exception e) {
        return Double.NaN; // return default value

      }

    }

    return ((Double)fieldObj).doubleValue();
  }

  ///////////////////////////////////////////////////////////////////////////
  /// Methods for "float" (from QuantBody.java.jpp)
  ///

  /** Returns the ith element of the array or collection argument.
   * If the argument is null or not an array or collection, returns a
   * default value (Float.NaN).
   **/
  /*@ pure */ public static float getElement_float (Object o, long i) {
    if (o == null) { return Float.NaN; } // return default value
    java.lang.Class c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getFloat (o, (int)i);
    } else if (o instanceof java.util.AbstractCollection) {
      return java.lang.reflect.Array.getFloat (((java.util.AbstractCollection)o).toArray(), (int)i);
    } else {
      return Float.NaN; // return default value
    }
  }

  /*@ pure */ public static float getElement_float (float[] arr, long i) {
    if (arr == null) { return Float.NaN; } // return default value
    return arr[(int)i];
  }

  private static boolean eq(float x, float y) {
    return fuzzy.eq(x,y);
  }

  private static boolean ne(float x, float y) {
    return fuzzy.ne(x,y);
  }

  private static boolean lt(float x, float y) {
    return fuzzy.lt(x,y);
  }

  private static boolean lte(float x, float y) {
    return fuzzy.lte(x,y);
  }

  private static boolean gt(float x, float y) {
    return fuzzy.gt(x,y);
  }

  private static boolean gte(float x, float y) {
    return fuzzy.gte(x,y);
  }

  /** True iff both sequences are non-null and have the same length. */
  /*@ pure */ public static boolean sameLength(float[] seq1, float[] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences are non-null and have the same length. */
  /*@ pure */ public static boolean sameLength(float[] seq1, double[] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences have the same length, and all seq2[i] divide seq1[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq2[i] divides seq1[i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean pairwiseDivides(float[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }
  /*@ pure */ public static boolean pairwiseDivides(float[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }

  /** True iff both sequences have the same length, and all seq1[i] ==  seq2[i] * seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] ==  seq2[i] * seq2[i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean pairwiseSquare(float[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i] * seq2[i])) {
        return false;
      }
    }
    return true;
  }
  /*@ pure */ public static boolean pairwiseSquare(float[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i] * seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] }
   * If either array is null, returns null.
   * If either array is empty, returns only those elements in the other array.
   * If both arrays are empty, returns a new empty array.
   */
  /*@ pure */ public static float[] concat(float[] seq1, float[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return utilMDE.ArraysMDE.concat(seq1, seq2);
  }

  /*@ pure */ public static double[] concat(float[] seq1, double[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    // Cannot just use utilMDE.ArraysMDE.concat because the two arrays
    // have different types.  This essentially inlines that method.
    int newLength = seq1.length + seq2.length;
    double[] retval = new double[newLength];
    System.arraycopy(seq1, 0, retval, 0, seq1.length);
    for (int j = 0 ; j < seq2.length ; j++) {
      retval[seq1.length+j] = seq2[j];
    }
    return retval;
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static float[] union(float[] seq1, float[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return concat(seq1, seq2);
  }

  /*@ pure */ public static double[] union(float[] seq1, double[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static float[] intersection(float[] seq1, float[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    float[] intermediate = new float[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /*@ pure */ public static double[] intersection(float[] seq1, double[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    double[] intermediate = new double[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static float[] setDiff(float[] seq1, float[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    float[] intermediate = new float[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /*@ pure */ public static double[] setDiff(float[] seq1, double[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    double[] intermediate = new double[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Retuns true iff seq1 and seq2 are equal when considered as sets.
   */
  /*@ pure */ public static boolean setEqual(float[] seq1, float[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean setEqual(float[] seq1, double[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean isReverse(float[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean isReverse(float[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /**
   * True iff seq1 is a subset of seq2, when the sequences are
   * considered as sets.
   */
  /*@ pure */ public static boolean subsetOf(float[] seq1, float[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean subsetOf(float[] seq1, double[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq contains no duplicate elements.
   */
  /*@ pure */ public static boolean noDups(float[] seq) {
    if (seq == null) { return false; }
    return utilMDE.ArraysMDE.noDuplicates(seq);
  }

 /**
  * Returns true iff elt is in array arr.
  */
  /*@ pure */ public static boolean memberOf(float elt, float[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /*@ pure */ public static boolean memberOf(float elt, double[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /**
   * Returns a subsequence of seq with first elements seq[start] and
   * last element seq[end].
   */
  /*@ pure */ public static float[] slice(float[] seq, int start, int end) {
    if (seq == null) { return null; }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) { return new float[] { }; }
    if (end > seq.length-1) { return new float[] { }; }
    if (sliceStart > sliceEnd) { return new float[] { }; }
    int length = sliceEnd - sliceStart + 1;
    return utilMDE.ArraysMDE.subarray(seq, sliceStart, length);
  }

  /*@ pure */ public static float[] slice(float[] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  /*@ pure */ public static float[] slice(float[] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  /*@ pure */ public static float[] slice(float[] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  /*@ pure */ public static boolean eltsEqual(float[] arr, float elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsEqual(float[] arr, double elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  /*@ pure */ public static boolean eltsNotEqual(float[] arr, float elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsNotEqual(float[] arr, double elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is greater than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] > elt
   *
   */
  /*@ pure */ public static boolean eltsGT(float[] arr, float elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsGT(float[] arr, double elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is greater than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] >= elt
   *
   */
  /*@ pure */ public static boolean eltsGTE(float[] arr, float elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsGTE(float[] arr, double elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is less than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] < elt
   *
   */
  /*@ pure */ public static boolean eltsLT(float[] arr, float elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsLT(float[] arr, double elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is less than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] <= elt
   *
   */
  /*@ pure */ public static boolean eltsLTE(float[] arr, float elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsLTE(float[] arr, double elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  /*@ pure */ public static boolean pairwiseEqual(float[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (Float.isNaN(seq1[i]) && Float.isNaN(seq2[i])) {
        continue;
      }
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseEqual(float[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseNotEqual(float[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseNotEqual(float[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] < seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] < seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseLT(float[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseLT(float[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] <= seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] <= seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseLTE(float[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseLTE(float[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] > seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] > seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseGT(float[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseGT(float[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] >= seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] >= seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseGTE(float[] seq1, float[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseGTE(float[] seq1, double[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  /*@ pure */ public static boolean lexEqual(float[] seq1, float[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, seq2);
  }

  /*@ pure */ public static boolean lexEqual(float[] seq1, double[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, seq2);
  }

  /**
   * Returns true iff seq1 is lexically not equal to seq2.
   */
  /*@ pure */ public static boolean lexNotEqual(float[] seq1, float[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return !lexEqual(seq1, seq2);
  }

  /*@ pure */ public static boolean lexNotEqual(float[] seq1, double[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return !lexEqual(seq1, seq2);
  }

  /**
   * Returns true iff seq1 is lexically <  seq2.
   */
  /*@ pure */ public static boolean lexLT(float[] seq1, float[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length >= seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexLT(float[] seq1, double[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
        } else if (lt(seq1[i], seq2[i])) {
          return true;
        }
      }
      if (seq1.length >= seq2.length) {
        return false;
      }
      return true;
    }

  /**
   * Returns true iff seq1 is lexically <= to seq2.
   */
  /*@ pure */ public static boolean lexLTE(float[] seq1, float[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexLTE(float[] seq1, double[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically > to seq2.
   */
  /*@ pure */ public static boolean lexGT(float[] seq1, float[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexGT(float[] seq1, double[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically >= to seq2.
   */
  /*@ pure */ public static boolean lexGTE(float[] seq1, float[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexGTE(float[] seq1, double[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseEqual(float[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (ne(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseNotEqual(float[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (eq(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] < seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] < seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseLT(float[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (gte(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] <= seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] <= seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseLTE(float[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (gt(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] > seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] > seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseGT(float[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (lte(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] >= seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] >= seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseGTE(float[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (lt(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] == i
   *
   */
  /*@ pure */ public static boolean eltsEqualIndex(float[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (ne(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] != i
   *
   */
  /*@ pure */ public static boolean eltsNotEqualIndex(float[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (eq(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] < i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] < i
   *
   */
  /*@ pure */ public static boolean eltsLtIndex(float[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] <= i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] <= i
   *
   */
  /*@ pure */ public static boolean eltsLteIndex(float[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] > i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] > i
   *
   */
  /*@ pure */ public static boolean  eltsGtIndex(float[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] >= i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] >= i
   *
   */
  /*@ pure */ public static boolean eltsGteIndex(float[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /// Deferencing (accessing) fields

  /**
   * collectfloat accepts an object and a list of fields (one of which is
   * of array type, and the rest of which are not), and produces an array in
   * which the original object has had the given fields accessed.
   *
   * Daikon creates invariants over "variables" such as the following.
   *
   * x.arr[].z   The result of collecting all elements y.z for all y's
   *             in array x.arr.
   * arr[].y.z   The result of collecting all elements x.y.z for all x's
   *             in array arr.
   * x.y.z[]     The result of collecting all elements in array x.y.z[]
   *
   * The collectfloat() method does this collecting work.
   *
   * Given an object (x, arr, or x, correspondingly, in the above examples)
   * and a "field string" (arr.z, y.z, or y.z, correspondingly, in the
   * above example), the collect method collects the elements the result
   * from following the fields, one of which is assumed to be an array.
   *
   * requires: fieldStr.length() > 0 and object != null
   * requires: fieldStr contains only field names, no "[]" strings.
   *
   * requires: the method only works for field sequences with exactly
   * one field representing an array. For example, the collection
   * a[].b[].c will fail.
   *
   * If the resulting collection is of non-primitive type, then collect
   * returns an array of type Object[].
   *
   * Returns null if any array or field access causes an exception.
   */
  /*@ pure */ public static float[] collectfloat (Object object, String fieldStr) {

    if (object == null) { return null; }
    if (fieldStr == null) { return null; }

    //Assert.assertTrue(fieldStr != null && !"".equals(fieldStr));
    String[] fieldNames = fieldStr.split("\\.");
    float[] retval = collectfloat (object, fieldNames, 0);
    //System.err.println("%%% fieldArray returned: " + utilMDE.ArraysMDE.toString(retval));
    return retval;
  }

  /** Helper method for collectfloat(Object, String).
   * Operates on the fields specified in fields[fieldsStartIdx..].
   * @see collectfloat(Object, String)
   */
  /*@ pure */ private static float[] collectfloat (Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) { return null; }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj = null;
    try {
      Field field = (object instanceof java.lang.Class)
        ? ((Class)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      //System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (float[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectfloat_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        //Assert.assertTrue(cls.equals(Float.TYPE));
        return new float[] { ((Float)fieldObj).floatValue() };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection collection = (java.util.AbstractCollection)fieldObj;
        float[] intermediate = new float[collection.size()];
        int index = 0;
        for (Iterator i = collection.iterator() ; i.hasNext() ; ) {
          float[] oneEltArray = collectfloat (i.next(), fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        float[] intermediate = new float[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          float[] oneEltArray = collectfloat (Array.get(fieldObj, i),
                                             fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectfloat (fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  // Returns the results of dereferencing the fields specified in
  // fields[fieldsStartIdx..] for 'object'.
  // Returns a default value if any field access causes an exception.
  /*@ pure */ public static float collectfloat_field (Object object, String fieldStr) {

    if (object == null) { return Float.NaN; } // return default value
    if (fieldStr == null) { return Float.NaN; } // return default value

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class)
          ? ((Class)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

      } catch (Exception e) {
        return Float.NaN; // return default value

      }

    }

    return ((Float)fieldObj).floatValue();
  }

  ///////////////////////////////////////////////////////////////////////////
  /// Methods for "int" (from QuantBody.java.jpp)
  ///

  /** Returns the ith element of the array or collection argument.
   * If the argument is null or not an array or collection, returns a
   * default value (Integer.MAX_VALUE).
   **/
  /*@ pure */ public static int getElement_int (Object o, long i) {
    if (o == null) { return Integer.MAX_VALUE; } // return default value
    java.lang.Class c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getInt (o, (int)i);
    } else if (o instanceof java.util.AbstractCollection) {
      return java.lang.reflect.Array.getInt (((java.util.AbstractCollection)o).toArray(), (int)i);
    } else {
      return Integer.MAX_VALUE; // return default value
    }
  }

  /*@ pure */ public static int getElement_int (int[] arr, long i) {
    if (arr == null) { return Integer.MAX_VALUE; } // return default value
    return arr[(int)i];
  }

  private static boolean eq(int x, int y) {
    return (x == y);
  }

  private static boolean ne(int x, int y) {
    return x != y;
  }

  private static boolean lt(int x, int y) {
    return x < y;
  }

  private static boolean lte(int x, int y) {
    return x <= y;
  }

  private static boolean gt(int x, int y) {
    return x > y;
  }

  private static boolean gte(int x, int y) {
    return x >= y;
  }

  /** True iff both sequences are non-null and have the same length. */
  /*@ pure */ public static boolean sameLength(int[] seq1, int[] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences are non-null and have the same length. */
  /*@ pure */ public static boolean sameLength(int[] seq1, long[] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences have the same length, and all seq2[i] divide seq1[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq2[i] divides seq1[i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean pairwiseDivides(int[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }
  /*@ pure */ public static boolean pairwiseDivides(int[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }

  /** True iff both sequences have the same length, and all seq1[i] ==  seq2[i] * seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] ==  seq2[i] * seq2[i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean pairwiseSquare(int[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i] * seq2[i])) {
        return false;
      }
    }
    return true;
  }
  /*@ pure */ public static boolean pairwiseSquare(int[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i] * seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff both sequences have the same length, and all seq1[i] == ~ seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] == ~ seq2[i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean pairwiseBitwiseComplement(int[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (seq1[i] != ~seq2[i]) {
        return false;
      }
    }
    return true;
  }
  /*@ pure */ public static boolean pairwiseBitwiseComplement(int[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (seq1[i] != ~seq2[i]) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseBitwiseComplement(Object[] seq1, Object[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    if (! eltsNonNull(seq1)) { return false; }
    if (! eltsNonNull(seq2)) { return false; }
    int[] hashArr1 = new int[seq1.length];
    for (int i = 0 ; i < seq1.length ; i++) {
      hashArr1[i] = seq1[i].hashCode();
    }
    int[] hashArr2 = new int[seq2.length];
    for (int i = 0 ; i < seq2.length ; i++) {
      hashArr2[i] = seq2[i].hashCode();
    }
    return pairwiseBitwiseComplement(hashArr1, hashArr2);
  }

  /** True iff both sequences have the same length, and all seq1[i] == (seq2[i] | seq1[i]).
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] == (seq2[i] | seq1[i])
   * </pre>
   *
   */
  /*@ pure */ public static boolean pairwiseBitwiseSubset(int[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    if (seq1.length != seq2.length) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], (seq2[i] | seq1[i]))) {
        return false;
      }
    }
    return true;
  }
  /*@ pure */ public static boolean pairwiseBitwiseSubset(int[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], (seq2[i] | seq1[i]))) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseBitwiseSubset(Object[] seq1, Object[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    if (! eltsNonNull(seq1)) { return false; }
    if (! eltsNonNull(seq2)) { return false; }
    int[] hashArr1 = new int[seq1.length];
    for (int i = 0 ; i < seq1.length ; i++) {
      hashArr1[i] = seq1[i].hashCode();
    }
    int[] hashArr2 = new int[seq2.length];
    for (int i = 0 ; i < seq2.length ; i++) {
      hashArr2[i] = seq2[i].hashCode();
    }
    return pairwiseBitwiseSubset(hashArr1, hashArr2);
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] }
   * If either array is null, returns null.
   * If either array is empty, returns only those elements in the other array.
   * If both arrays are empty, returns a new empty array.
   */
  /*@ pure */ public static int[] concat(int[] seq1, int[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return utilMDE.ArraysMDE.concat(seq1, seq2);
  }

  /*@ pure */ public static long[] concat(int[] seq1, long[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    // Cannot just use utilMDE.ArraysMDE.concat because the two arrays
    // have different types.  This essentially inlines that method.
    int newLength = seq1.length + seq2.length;
    long[] retval = new long[newLength];
    System.arraycopy(seq1, 0, retval, 0, seq1.length);
    for (int j = 0 ; j < seq2.length ; j++) {
      retval[seq1.length+j] = seq2[j];
    }
    return retval;
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static int[] union(int[] seq1, int[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return concat(seq1, seq2);
  }

  /*@ pure */ public static long[] union(int[] seq1, long[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static int[] intersection(int[] seq1, int[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    int[] intermediate = new int[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /*@ pure */ public static long[] intersection(int[] seq1, long[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    long[] intermediate = new long[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static int[] setDiff(int[] seq1, int[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    int[] intermediate = new int[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /*@ pure */ public static long[] setDiff(int[] seq1, long[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    long[] intermediate = new long[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Retuns true iff seq1 and seq2 are equal when considered as sets.
   */
  /*@ pure */ public static boolean setEqual(int[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean setEqual(int[] seq1, long[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean isReverse(int[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean isReverse(int[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /** True iff all elements in elts occur once or more in arr;
   * that is, elts is a subset of arr.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..elt.length-1 } : elt[i] element_of arr
   *
   */
  /*@ pure */ public static boolean subsetOf(Object elts, Object arr) {
    if (arr == null) { return false; }
    if (elts == null) { return false; }
    if (!(elts.getClass().isArray() && arr.getClass().isArray())) {
      //throw new IllegalArgumentException("both arguments must be arrays.");
      return false;
    }
    // We know that the two arguments are arrays of different types; if
    // they had been of the same type, then one of the more specific
    // overriding versions of this method would have been called.

    // This implementation simply calls either subsetOf(long[], long[]) or
    // subsetOf(double[], double[]).

    Class eltsType = elts.getClass().getComponentType();
    Class arrType = arr.getClass().getComponentType();
    if (isIntegralType(eltsType) && isIntegralType(arrType)) {
      // Both arrays are int/long
      // Cast both arrays to long and call subsetOf(long[],long[])
      long[] elts_long;
      if (eltsType == Long.class) {
        elts_long = (long[]) elts;
      } else {
        elts_long = new long[Array.getLength(elts)];
        for (int i = 0 ; i < elts_long.length ; i++) {
          elts_long[i] = Array.getLong(elts, i);
        }
      }
      long[] arr_long;
      if (arrType == Long.class) {
        arr_long = (long[]) arr;
      } else {
        arr_long = new long[Array.getLength(arr)];
        for (int i = 0 ; i < arr_long.length ; i++) {
          arr_long[i] = Array.getLong(arr, i);
        }
      }
      return subsetOf(elts_long, arr_long);
    } else if (isNumericType(eltsType) && isNumericType(arrType)) {
      // At least one array is float/double.
      // Cast both arrays to double and call subsetOf(double[],double[])
      double[] elts_double = new double[Array.getLength(elts)];
      for (int i = 0 ; i < elts_double.length ; i++) {
        elts_double[i] = Array.getDouble(elts, i);
      }
      double[] arr_double = new double[Array.getLength(arr)];
      for (int i = 0 ; i < arr_double.length ; i++) {
        arr_double[i] = Array.getDouble(arr, i);
      }
      return subsetOf(elts_double, arr_double);
    } else {
      // throw new IllegalArgumentException("both arguments must be arrays of numeric types.");
      return false;
    }

  }

  /**
   * True iff seq1 is a subset of seq2, when the sequences are
   * considered as sets.
   */
  /*@ pure */ public static boolean subsetOf(int[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean subsetOf(int[] seq1, long[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq contains no duplicate elements.
   */
  /*@ pure */ public static boolean noDups(int[] seq) {
    if (seq == null) { return false; }
    return utilMDE.ArraysMDE.noDuplicates(seq);
  }

 /**
  * Returns true iff elt is in array arr.
  */
  /*@ pure */ public static boolean memberOf(int elt, int[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /*@ pure */ public static boolean memberOf(int elt, long[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /**
   * Returns a subsequence of seq with first elements seq[start] and
   * last element seq[end].
   */
  /*@ pure */ public static int[] slice(int[] seq, int start, int end) {
    if (seq == null) { return null; }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) { return new int[] { }; }
    if (end > seq.length-1) { return new int[] { }; }
    if (sliceStart > sliceEnd) { return new int[] { }; }
    int length = sliceEnd - sliceStart + 1;
    return utilMDE.ArraysMDE.subarray(seq, sliceStart, length);
  }

  /*@ pure */ public static int[] slice(int[] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  /*@ pure */ public static int[] slice(int[] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  /*@ pure */ public static int[] slice(int[] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  /*@ pure */ public static boolean eltsEqual(int[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsEqual(int[] arr, long elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  /*@ pure */ public static boolean eltsNotEqual(int[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsNotEqual(int[] arr, long elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is greater than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] > elt
   *
   */
  /*@ pure */ public static boolean eltsGT(int[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsGT(int[] arr, long elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is greater than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] >= elt
   *
   */
  /*@ pure */ public static boolean eltsGTE(int[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsGTE(int[] arr, long elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is less than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] < elt
   *
   */
  /*@ pure */ public static boolean eltsLT(int[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsLT(int[] arr, long elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is less than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] <= elt
   *
   */
  /*@ pure */ public static boolean eltsLTE(int[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsLTE(int[] arr, long elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  /*@ pure */ public static boolean pairwiseEqual(int[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseEqual(int[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseNotEqual(int[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseNotEqual(int[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] < seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] < seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseLT(int[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseLT(int[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] <= seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] <= seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseLTE(int[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseLTE(int[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] > seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] > seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseGT(int[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseGT(int[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] >= seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] >= seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseGTE(int[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseGTE(int[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  /*@ pure */ public static boolean lexEqual(int[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, seq2);
  }

  /*@ pure */ public static boolean lexEqual(int[] seq1, long[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, seq2);
  }

  /**
   * Returns true iff seq1 is lexically not equal to seq2.
   */
  /*@ pure */ public static boolean lexNotEqual(int[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return !lexEqual(seq1, seq2);
  }

  /*@ pure */ public static boolean lexNotEqual(int[] seq1, long[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return !lexEqual(seq1, seq2);
  }

  /**
   * Returns true iff seq1 is lexically <  seq2.
   */
  /*@ pure */ public static boolean lexLT(int[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length >= seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexLT(int[] seq1, long[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
        } else if (lt(seq1[i], seq2[i])) {
          return true;
        }
      }
      if (seq1.length >= seq2.length) {
        return false;
      }
      return true;
    }

  /**
   * Returns true iff seq1 is lexically <= to seq2.
   */
  /*@ pure */ public static boolean lexLTE(int[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexLTE(int[] seq1, long[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically > to seq2.
   */
  /*@ pure */ public static boolean lexGT(int[] seq1, int[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexGT(int[] seq1, long[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically >= to seq2.
   */
  /*@ pure */ public static boolean lexGTE(int[] seq1, int[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexGTE(int[] seq1, long[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseEqual(int[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (ne(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseNotEqual(int[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (eq(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] < seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] < seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseLT(int[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (gte(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] <= seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] <= seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseLTE(int[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (gt(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] > seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] > seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseGT(int[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (lte(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] >= seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] >= seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseGTE(int[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (lt(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] == i
   *
   */
  /*@ pure */ public static boolean eltsEqualIndex(int[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (ne(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] != i
   *
   */
  /*@ pure */ public static boolean eltsNotEqualIndex(int[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (eq(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] < i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] < i
   *
   */
  /*@ pure */ public static boolean eltsLtIndex(int[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] <= i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] <= i
   *
   */
  /*@ pure */ public static boolean eltsLteIndex(int[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] > i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] > i
   *
   */
  /*@ pure */ public static boolean  eltsGtIndex(int[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] >= i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] >= i
   *
   */
  /*@ pure */ public static boolean eltsGteIndex(int[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /// Deferencing (accessing) fields

  /**
   * collectint accepts an object and a list of fields (one of which is
   * of array type, and the rest of which are not), and produces an array in
   * which the original object has had the given fields accessed.
   *
   * Daikon creates invariants over "variables" such as the following.
   *
   * x.arr[].z   The result of collecting all elements y.z for all y's
   *             in array x.arr.
   * arr[].y.z   The result of collecting all elements x.y.z for all x's
   *             in array arr.
   * x.y.z[]     The result of collecting all elements in array x.y.z[]
   *
   * The collectint() method does this collecting work.
   *
   * Given an object (x, arr, or x, correspondingly, in the above examples)
   * and a "field string" (arr.z, y.z, or y.z, correspondingly, in the
   * above example), the collect method collects the elements the result
   * from following the fields, one of which is assumed to be an array.
   *
   * requires: fieldStr.length() > 0 and object != null
   * requires: fieldStr contains only field names, no "[]" strings.
   *
   * requires: the method only works for field sequences with exactly
   * one field representing an array. For example, the collection
   * a[].b[].c will fail.
   *
   * If the resulting collection is of non-primitive type, then collect
   * returns an array of type Object[].
   *
   * Returns null if any array or field access causes an exception.
   */
  /*@ pure */ public static int[] collectint (Object object, String fieldStr) {

    if (object == null) { return null; }
    if (fieldStr == null) { return null; }

    //Assert.assertTrue(fieldStr != null && !"".equals(fieldStr));
    String[] fieldNames = fieldStr.split("\\.");
    int[] retval = collectint (object, fieldNames, 0);
    //System.err.println("%%% fieldArray returned: " + utilMDE.ArraysMDE.toString(retval));
    return retval;
  }

  /** Helper method for collectint(Object, String).
   * Operates on the fields specified in fields[fieldsStartIdx..].
   * @see collectint(Object, String)
   */
  /*@ pure */ private static int[] collectint (Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) { return null; }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj = null;
    try {
      Field field = (object instanceof java.lang.Class)
        ? ((Class)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      //System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (int[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectint_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        //Assert.assertTrue(cls.equals(Integer.TYPE));
        return new int[] { ((Integer)fieldObj).intValue() };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection collection = (java.util.AbstractCollection)fieldObj;
        int[] intermediate = new int[collection.size()];
        int index = 0;
        for (Iterator i = collection.iterator() ; i.hasNext() ; ) {
          int[] oneEltArray = collectint (i.next(), fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        int[] intermediate = new int[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          int[] oneEltArray = collectint (Array.get(fieldObj, i),
                                             fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectint (fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  // Returns the results of dereferencing the fields specified in
  // fields[fieldsStartIdx..] for 'object'.
  // Returns a default value if any field access causes an exception.
  /*@ pure */ public static int collectint_field (Object object, String fieldStr) {

    if (object == null) { return Integer.MAX_VALUE; } // return default value
    if (fieldStr == null) { return Integer.MAX_VALUE; } // return default value

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class)
          ? ((Class)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

      } catch (Exception e) {
        return Integer.MAX_VALUE; // return default value

      }

    }

    return ((Integer)fieldObj).intValue();
  }

  ///////////////////////////////////////////////////////////////////////////
  /// Methods for "long" (from QuantBody.java.jpp)
  ///

  /** Returns the ith element of the array or collection argument.
   * If the argument is null or not an array or collection, returns a
   * default value (Long.MAX_VALUE).
   **/
  /*@ pure */ public static long getElement_long (Object o, long i) {
    if (o == null) { return Long.MAX_VALUE; } // return default value
    java.lang.Class c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getLong (o, (int)i);
    } else if (o instanceof java.util.AbstractCollection) {
      return java.lang.reflect.Array.getLong (((java.util.AbstractCollection)o).toArray(), (int)i);
    } else {
      return Long.MAX_VALUE; // return default value
    }
  }

  /*@ pure */ public static long getElement_long (long[] arr, long i) {
    if (arr == null) { return Long.MAX_VALUE; } // return default value
    return arr[(int)i];
  }

  private static boolean eq(long x, long y) {
    return (x == y);
  }

  private static boolean ne(long x, long y) {
    return x != y;
  }

  private static boolean lt(long x, long y) {
    return x < y;
  }

  private static boolean lte(long x, long y) {
    return x <= y;
  }

  private static boolean gt(long x, long y) {
    return x > y;
  }

  private static boolean gte(long x, long y) {
    return x >= y;
  }

  /** True iff both sequences are non-null and have the same length. */
  /*@ pure */ public static boolean sameLength(long[] seq1, long[] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences are non-null and have the same length. */
  /*@ pure */ public static boolean sameLength(long[] seq1, int[] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /** True iff both sequences have the same length, and all seq2[i] divide seq1[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq2[i] divides seq1[i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean pairwiseDivides(long[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }
  /*@ pure */ public static boolean pairwiseDivides(long[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i] % seq2[i], 0)) {
        return false;
      }
    }
    return true;
  }

  /** True iff both sequences have the same length, and all seq1[i] ==  seq2[i] * seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] ==  seq2[i] * seq2[i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean pairwiseSquare(long[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i] * seq2[i])) {
        return false;
      }
    }
    return true;
  }
  /*@ pure */ public static boolean pairwiseSquare(long[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i] * seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff both sequences have the same length, and all seq1[i] == ~ seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] == ~ seq2[i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean pairwiseBitwiseComplement(long[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (seq1[i] != ~seq2[i]) {
        return false;
      }
    }
    return true;
  }
  /*@ pure */ public static boolean pairwiseBitwiseComplement(long[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (seq1[i] != ~seq2[i]) {
        return false;
      }
    }
    return true;
  }

  /** True iff both sequences have the same length, and all seq1[i] == (seq2[i] | seq1[i]).
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq2.length-1 } : seq1[i] == (seq2[i] | seq1[i])
   * </pre>
   *
   */
  /*@ pure */ public static boolean pairwiseBitwiseSubset(long[] seq1, long[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    if (seq1.length != seq2.length) {
      return false;
    }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], (seq2[i] | seq1[i]))) {
        return false;
      }
    }
    return true;
  }
  /*@ pure */ public static boolean pairwiseBitwiseSubset(long[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], (seq2[i] | seq1[i]))) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] }
   * If either array is null, returns null.
   * If either array is empty, returns only those elements in the other array.
   * If both arrays are empty, returns a new empty array.
   */
  /*@ pure */ public static long[] concat(long[] seq1, long[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return utilMDE.ArraysMDE.concat(seq1, seq2);
  }

  /*@ pure */ public static long[] concat(long[] seq1, int[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    // Cannot just use utilMDE.ArraysMDE.concat because the two arrays
    // have different types.  This essentially inlines that method.
    int newLength = seq1.length + seq2.length;
    long[] retval = new long[newLength];
    System.arraycopy(seq1, 0, retval, 0, seq1.length);
    for (int j = 0 ; j < seq2.length ; j++) {
      retval[seq1.length+j] = seq2[j];
    }
    return retval;
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static long[] union(long[] seq1, long[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return concat(seq1, seq2);
  }

  /*@ pure */ public static long[] union(long[] seq1, int[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static long[] intersection(long[] seq1, long[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    long[] intermediate = new long[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /*@ pure */ public static long[] intersection(long[] seq1, int[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    long[] intermediate = new long[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static long[] setDiff(long[] seq1, long[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    long[] intermediate = new long[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /*@ pure */ public static long[] setDiff(long[] seq1, int[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    long[] intermediate = new long[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Retuns true iff seq1 and seq2 are equal when considered as sets.
   */
  /*@ pure */ public static boolean setEqual(long[] seq1, long[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean setEqual(long[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean isReverse(long[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean isReverse(long[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /**
   * True iff seq1 is a subset of seq2, when the sequences are
   * considered as sets.
   */
  /*@ pure */ public static boolean subsetOf(long[] seq1, long[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean subsetOf(long[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq contains no duplicate elements.
   */
  /*@ pure */ public static boolean noDups(long[] seq) {
    if (seq == null) { return false; }
    return utilMDE.ArraysMDE.noDuplicates(seq);
  }

 /**
  * Returns true iff elt is in array arr.
  */
  /*@ pure */ public static boolean memberOf(long elt, long[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /*@ pure */ public static boolean memberOf(long elt, int[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /**
   * Returns a subsequence of seq with first elements seq[start] and
   * last element seq[end].
   */
  /*@ pure */ public static long[] slice(long[] seq, int start, int end) {
    if (seq == null) { return null; }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) { return new long[] { }; }
    if (end > seq.length-1) { return new long[] { }; }
    if (sliceStart > sliceEnd) { return new long[] { }; }
    int length = sliceEnd - sliceStart + 1;
    return utilMDE.ArraysMDE.subarray(seq, sliceStart, length);
  }

  /*@ pure */ public static long[] slice(long[] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  /*@ pure */ public static long[] slice(long[] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  /*@ pure */ public static long[] slice(long[] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  /*@ pure */ public static boolean eltsEqual(long[] arr, long elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsEqual(long[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  /*@ pure */ public static boolean eltsNotEqual(long[] arr, long elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsNotEqual(long[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is greater than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] > elt
   *
   */
  /*@ pure */ public static boolean eltsGT(long[] arr, long elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsGT(long[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is greater than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] >= elt
   *
   */
  /*@ pure */ public static boolean eltsGTE(long[] arr, long elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsGTE(long[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is less than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] < elt
   *
   */
  /*@ pure */ public static boolean eltsLT(long[] arr, long elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsLT(long[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is less than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] <= elt
   *
   */
  /*@ pure */ public static boolean eltsLTE(long[] arr, long elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /*@ pure */ public static boolean eltsLTE(long[] arr, int elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  /*@ pure */ public static boolean pairwiseEqual(long[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseEqual(long[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseNotEqual(long[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseNotEqual(long[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] < seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] < seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseLT(long[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseLT(long[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] <= seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] <= seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseLTE(long[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseLTE(long[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] > seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] > seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseGT(long[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseGT(long[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] >= seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] >= seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseGTE(long[] seq1, long[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseGTE(long[] seq1, int[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  /*@ pure */ public static boolean lexEqual(long[] seq1, long[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, seq2);
  }

  /*@ pure */ public static boolean lexEqual(long[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, seq2);
  }

  /**
   * Returns true iff seq1 is lexically not equal to seq2.
   */
  /*@ pure */ public static boolean lexNotEqual(long[] seq1, long[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return !lexEqual(seq1, seq2);
  }

  /*@ pure */ public static boolean lexNotEqual(long[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return !lexEqual(seq1, seq2);
  }

  /**
   * Returns true iff seq1 is lexically <  seq2.
   */
  /*@ pure */ public static boolean lexLT(long[] seq1, long[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length >= seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexLT(long[] seq1, int[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
        } else if (lt(seq1[i], seq2[i])) {
          return true;
        }
      }
      if (seq1.length >= seq2.length) {
        return false;
      }
      return true;
    }

  /**
   * Returns true iff seq1 is lexically <= to seq2.
   */
  /*@ pure */ public static boolean lexLTE(long[] seq1, long[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexLTE(long[] seq1, int[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically > to seq2.
   */
  /*@ pure */ public static boolean lexGT(long[] seq1, long[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexGT(long[] seq1, int[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically >= to seq2.
   */
  /*@ pure */ public static boolean lexGTE(long[] seq1, long[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /*@ pure */ public static boolean lexGTE(long[] seq1, int[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseEqual(long[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (ne(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseNotEqual(long[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (eq(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] < seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] < seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseLT(long[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (gte(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] <= seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] <= seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseLTE(long[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (gt(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] > seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] > seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseGT(long[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (lte(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] >= seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] >= seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseGTE(long[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (lt(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] == i
   *
   */
  /*@ pure */ public static boolean eltsEqualIndex(long[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (ne(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] != i
   *
   */
  /*@ pure */ public static boolean eltsNotEqualIndex(long[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (eq(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] < i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] < i
   *
   */
  /*@ pure */ public static boolean eltsLtIndex(long[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] <= i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] <= i
   *
   */
  /*@ pure */ public static boolean eltsLteIndex(long[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] > i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] > i
   *
   */
  /*@ pure */ public static boolean  eltsGtIndex(long[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] >= i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] >= i
   *
   */
  /*@ pure */ public static boolean eltsGteIndex(long[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /// Deferencing (accessing) fields

  /**
   * collectlong accepts an object and a list of fields (one of which is
   * of array type, and the rest of which are not), and produces an array in
   * which the original object has had the given fields accessed.
   *
   * Daikon creates invariants over "variables" such as the following.
   *
   * x.arr[].z   The result of collecting all elements y.z for all y's
   *             in array x.arr.
   * arr[].y.z   The result of collecting all elements x.y.z for all x's
   *             in array arr.
   * x.y.z[]     The result of collecting all elements in array x.y.z[]
   *
   * The collectlong() method does this collecting work.
   *
   * Given an object (x, arr, or x, correspondingly, in the above examples)
   * and a "field string" (arr.z, y.z, or y.z, correspondingly, in the
   * above example), the collect method collects the elements the result
   * from following the fields, one of which is assumed to be an array.
   *
   * requires: fieldStr.length() > 0 and object != null
   * requires: fieldStr contains only field names, no "[]" strings.
   *
   * requires: the method only works for field sequences with exactly
   * one field representing an array. For example, the collection
   * a[].b[].c will fail.
   *
   * If the resulting collection is of non-primitive type, then collect
   * returns an array of type Object[].
   *
   * Returns null if any array or field access causes an exception.
   */
  /*@ pure */ public static long[] collectlong (Object object, String fieldStr) {

    if (object == null) { return null; }
    if (fieldStr == null) { return null; }

    //Assert.assertTrue(fieldStr != null && !"".equals(fieldStr));
    String[] fieldNames = fieldStr.split("\\.");
    long[] retval = collectlong (object, fieldNames, 0);
    //System.err.println("%%% fieldArray returned: " + utilMDE.ArraysMDE.toString(retval));
    return retval;
  }

  /** Helper method for collectlong(Object, String).
   * Operates on the fields specified in fields[fieldsStartIdx..].
   * @see collectlong(Object, String)
   */
  /*@ pure */ private static long[] collectlong (Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) { return null; }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj = null;
    try {
      Field field = (object instanceof java.lang.Class)
        ? ((Class)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      //System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (long[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectlong_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        //Assert.assertTrue(cls.equals(Long.TYPE));
        return new long[] { ((Long)fieldObj).longValue() };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection collection = (java.util.AbstractCollection)fieldObj;
        long[] intermediate = new long[collection.size()];
        int index = 0;
        for (Iterator i = collection.iterator() ; i.hasNext() ; ) {
          long[] oneEltArray = collectlong (i.next(), fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        long[] intermediate = new long[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          long[] oneEltArray = collectlong (Array.get(fieldObj, i),
                                             fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectlong (fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  // Returns the results of dereferencing the fields specified in
  // fields[fieldsStartIdx..] for 'object'.
  // Returns a default value if any field access causes an exception.
  /*@ pure */ public static long collectlong_field (Object object, String fieldStr) {

    if (object == null) { return Long.MAX_VALUE; } // return default value
    if (fieldStr == null) { return Long.MAX_VALUE; } // return default value

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class)
          ? ((Class)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

      } catch (Exception e) {
        return Long.MAX_VALUE; // return default value

      }

    }

    return ((Long)fieldObj).longValue();
  }

  ///////////////////////////////////////////////////////////////////////////
  /// Methods for "short" (from QuantBody.java.jpp)
  ///

  /** Returns the ith element of the array or collection argument.
   * If the argument is null or not an array or collection, returns a
   * default value (Short.MAX_VALUE).
   **/
  /*@ pure */ public static short getElement_short (Object o, long i) {
    if (o == null) { return Short.MAX_VALUE; } // return default value
    java.lang.Class c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.getShort (o, (int)i);
    } else if (o instanceof java.util.AbstractCollection) {
      return java.lang.reflect.Array.getShort (((java.util.AbstractCollection)o).toArray(), (int)i);
    } else {
      return Short.MAX_VALUE; // return default value
    }
  }

  /*@ pure */ public static short getElement_short (short[] arr, long i) {
    if (arr == null) { return Short.MAX_VALUE; } // return default value
    return arr[(int)i];
  }

  private static boolean eq(short x, short y) {
    return (x == y);
  }

  private static boolean ne(short x, short y) {
    return x != y;
  }

  private static boolean lt(short x, short y) {
    return x < y;
  }

  private static boolean lte(short x, short y) {
    return x <= y;
  }

  private static boolean gt(short x, short y) {
    return x > y;
  }

  private static boolean gte(short x, short y) {
    return x >= y;
  }

  /** True iff both sequences are non-null and have the same length. */
  /*@ pure */ public static boolean sameLength(short[] seq1, short[] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] }
   * If either array is null, returns null.
   * If either array is empty, returns only those elements in the other array.
   * If both arrays are empty, returns a new empty array.
   */
  /*@ pure */ public static short[] concat(short[] seq1, short[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return utilMDE.ArraysMDE.concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static short[] union(short[] seq1, short[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static short[] intersection(short[] seq1, short[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    short[] intermediate = new short[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static short[] setDiff(short[] seq1, short[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    short[] intermediate = new short[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Retuns true iff seq1 and seq2 are equal when considered as sets.
   */
  /*@ pure */ public static boolean setEqual(short[] seq1, short[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean isReverse(short[] seq1, short[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /**
   * True iff seq1 is a subset of seq2, when the sequences are
   * considered as sets.
   */
  /*@ pure */ public static boolean subsetOf(short[] seq1, short[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq contains no duplicate elements.
   */
  /*@ pure */ public static boolean noDups(short[] seq) {
    if (seq == null) { return false; }
    return utilMDE.ArraysMDE.noDuplicates(seq);
  }

 /**
  * Returns true iff elt is in array arr.
  */
  /*@ pure */ public static boolean memberOf(short elt, short[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /*@ pure */ public static boolean memberOf(long elt, short[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /**
   * Returns a subsequence of seq with first elements seq[start] and
   * last element seq[end].
   */
  /*@ pure */ public static short[] slice(short[] seq, int start, int end) {
    if (seq == null) { return null; }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) { return new short[] { }; }
    if (end > seq.length-1) { return new short[] { }; }
    if (sliceStart > sliceEnd) { return new short[] { }; }
    int length = sliceEnd - sliceStart + 1;
    return utilMDE.ArraysMDE.subarray(seq, sliceStart, length);
  }

  /*@ pure */ public static short[] slice(short[] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  /*@ pure */ public static short[] slice(short[] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  /*@ pure */ public static short[] slice(short[] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  /*@ pure */ public static boolean eltsEqual(short[] arr, short elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  /*@ pure */ public static boolean eltsNotEqual(short[] arr, short elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is greater than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] > elt
   *
   */
  /*@ pure */ public static boolean eltsGT(short[] arr, short elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is greater than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] >= elt
   *
   */
  /*@ pure */ public static boolean eltsGTE(short[] arr, short elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (lt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is less than elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] < elt
   *
   */
  /*@ pure */ public static boolean eltsLT(short[] arr, short elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gte(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr is less than or equal to elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] <= elt
   *
   */
  /*@ pure */ public static boolean eltsLTE(short[] arr, short elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (gt(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  /*@ pure */ public static boolean pairwiseEqual(short[] seq1, short[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseNotEqual(short[] seq1, short[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] < seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] < seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseLT(short[] seq1, short[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] <= seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] <= seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseLTE(short[] seq1, short[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] > seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] > seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseGT(short[] seq1, short[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lte(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] >= seq2[i].
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] >= seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseGTE(short[] seq1, short[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  /*@ pure */ public static boolean lexEqual(short[] seq1, short[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, seq2);
  }

  /**
   * Returns true iff seq1 is lexically not equal to seq2.
   */
  /*@ pure */ public static boolean lexNotEqual(short[] seq1, short[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return !lexEqual(seq1, seq2);
  }

  /**
   * Returns true iff seq1 is lexically <  seq2.
   */
  /*@ pure */ public static boolean lexLT(short[] seq1, short[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length >= seq2.length) {
      return false;
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically <= to seq2.
   */
  /*@ pure */ public static boolean lexLTE(short[] seq1, short[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (gt(seq1[i], seq2[i])) {
        return false;
      } else if (lt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length > seq2.length) {
      return false;
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically > to seq2.
   */
  /*@ pure */ public static boolean lexGT(short[] seq1, short[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length <= seq2.length) {
      return false;
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically >= to seq2.
   */
  /*@ pure */ public static boolean lexGTE(short[] seq1, short[] seq2) {
  if (seq1 == null) { return false; }
  if (seq2 == null) { return false; }
    int minlength = (seq1.length < seq2.length) ? seq1.length : seq2.length;
    for (int i = 0 ; i < minlength ; i++) {
      if (lt(seq1[i], seq2[i])) {
        return false;
      } else if (gt(seq1[i], seq2[i])) {
        return true;
      }
    }
    if (seq1.length < seq2.length) {
      return false;
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseEqual(short[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (ne(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseNotEqual(short[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (eq(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] < seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] < seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseLT(short[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (gte(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] <= seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] <= seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseLTE(short[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (gt(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] > seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] > seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseGT(short[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (lte(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] >= seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] >= seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseGTE(short[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (lt(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] == i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] == i
   *
   */
  /*@ pure */ public static boolean eltsEqualIndex(short[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (ne(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] != i
   *
   */
  /*@ pure */ public static boolean eltsNotEqualIndex(short[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (eq(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] < i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] < i
   *
   */
  /*@ pure */ public static boolean eltsLtIndex(short[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] <= i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] <= i
   *
   */
  /*@ pure */ public static boolean eltsLteIndex(short[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (gt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] > i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] > i
   *
   */
  /*@ pure */ public static boolean  eltsGtIndex(short[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lte(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] >= i.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-1 } : seq[i] >= i
   *
   */
  /*@ pure */ public static boolean eltsGteIndex(short[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (lt(seq[i], i)) {
        return false;
      }
    }
    return true;
  }

  /// Deferencing (accessing) fields

  /**
   * collectshort accepts an object and a list of fields (one of which is
   * of array type, and the rest of which are not), and produces an array in
   * which the original object has had the given fields accessed.
   *
   * Daikon creates invariants over "variables" such as the following.
   *
   * x.arr[].z   The result of collecting all elements y.z for all y's
   *             in array x.arr.
   * arr[].y.z   The result of collecting all elements x.y.z for all x's
   *             in array arr.
   * x.y.z[]     The result of collecting all elements in array x.y.z[]
   *
   * The collectshort() method does this collecting work.
   *
   * Given an object (x, arr, or x, correspondingly, in the above examples)
   * and a "field string" (arr.z, y.z, or y.z, correspondingly, in the
   * above example), the collect method collects the elements the result
   * from following the fields, one of which is assumed to be an array.
   *
   * requires: fieldStr.length() > 0 and object != null
   * requires: fieldStr contains only field names, no "[]" strings.
   *
   * requires: the method only works for field sequences with exactly
   * one field representing an array. For example, the collection
   * a[].b[].c will fail.
   *
   * If the resulting collection is of non-primitive type, then collect
   * returns an array of type Object[].
   *
   * Returns null if any array or field access causes an exception.
   */
  /*@ pure */ public static short[] collectshort (Object object, String fieldStr) {

    if (object == null) { return null; }
    if (fieldStr == null) { return null; }

    //Assert.assertTrue(fieldStr != null && !"".equals(fieldStr));
    String[] fieldNames = fieldStr.split("\\.");
    short[] retval = collectshort (object, fieldNames, 0);
    //System.err.println("%%% fieldArray returned: " + utilMDE.ArraysMDE.toString(retval));
    return retval;
  }

  /** Helper method for collectshort(Object, String).
   * Operates on the fields specified in fields[fieldsStartIdx..].
   * @see collectshort(Object, String)
   */
  /*@ pure */ private static short[] collectshort (Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) { return null; }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj = null;
    try {
      Field field = (object instanceof java.lang.Class)
        ? ((Class)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      //System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (short[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectshort_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        //Assert.assertTrue(cls.equals(Short.TYPE));
        return new short[] { ((Short)fieldObj).shortValue() };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection collection = (java.util.AbstractCollection)fieldObj;
        short[] intermediate = new short[collection.size()];
        int index = 0;
        for (Iterator i = collection.iterator() ; i.hasNext() ; ) {
          short[] oneEltArray = collectshort (i.next(), fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        short[] intermediate = new short[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          short[] oneEltArray = collectshort (Array.get(fieldObj, i),
                                             fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectshort (fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  // Returns the results of dereferencing the fields specified in
  // fields[fieldsStartIdx..] for 'object'.
  // Returns a default value if any field access causes an exception.
  /*@ pure */ public static short collectshort_field (Object object, String fieldStr) {

    if (object == null) { return Short.MAX_VALUE; } // return default value
    if (fieldStr == null) { return Short.MAX_VALUE; } // return default value

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class)
          ? ((Class)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

      } catch (Exception e) {
        return Short.MAX_VALUE; // return default value

      }

    }

    return ((Short)fieldObj).shortValue();
  }

  ///////////////////////////////////////////////////////////////////////////
  /// Methods for "Object" (from QuantBody.java.jpp)
  ///

  /** Returns the ith element of the array or collection argument.
   * If the argument is null or not an array or collection, returns a
   * default value (null).
   **/
  /*@ pure */ public static Object getElement_Object (Object o, long i) {
    if (o == null) { return null; } // return default value
    java.lang.Class c = o.getClass();
    if (c.isArray()) {
      return java.lang.reflect.Array.get (o, (int)i);
    } else if (o instanceof java.util.AbstractCollection) {
      return java.lang.reflect.Array.get (((java.util.AbstractCollection)o).toArray(), (int)i);
    } else {
      return null; // return default value
    }
  }

  /*@ pure */ public static Object getElement_Object (Object[] arr, long i) {
    if (arr == null) { return null; } // return default value
    return arr[(int)i];
  }

  private static boolean eq(Object x, Object y) {
    return (x == y);
  }

  private static boolean ne(Object x, Object y) {
    return x != y;
  }

  /** Returns an array of Strings, where the strings are the result of
   * invoking x.getClass().toString() for each element x in the
   * array. If an element of the array is null, its slot in the returned
   * array is null.
   */
  /*@ pure */ public static String[] typeArray(Object[] seq) {
    if (seq == null) { return null; }
    String[] retval = new String[seq.length];
    for (int i = 0 ; i < seq.length ; i++) {
      if (seq[i] == null) {
        retval[i] = null;
      } else {
        retval[i] = seq[i].getClass().toString();
      }
    }
    return retval;
  }

  /** True iff both sequences are non-null and have the same length. */
  /*@ pure */ public static boolean sameLength(Object[] seq1, Object[] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] }
   * If either array is null, returns null.
   * If either array is empty, returns only those elements in the other array.
   * If both arrays are empty, returns a new empty array.
   */
  /*@ pure */ public static Object[] concat(Object[] seq1, Object[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return utilMDE.ArraysMDE.concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static Object[] union(Object[] seq1, Object[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static Object[] intersection(Object[] seq1, Object[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    Object[] intermediate = new Object[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static Object[] setDiff(Object[] seq1, Object[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    Object[] intermediate = new Object[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Retuns true iff seq1 and seq2 are equal when considered as sets.
   */
  /*@ pure */ public static boolean setEqual(Object[] seq1, Object[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean isReverse(Object[] seq1, Object[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean isReverse(Collection seq1, Object[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    Object[] seq1_array = seq1.toArray(new Object[]{});
    return isReverse(seq1_array, seq2);
  }

  /**
   * True iff seq1 is a subset of seq2, when the sequences are
   * considered as sets.
   */
  /*@ pure */ public static boolean subsetOf(Object[] seq1, Object[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean subsetOf(Collection seq1, Object[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    Object[] seq1_array = seq1.toArray(new Object[]{});
    return subsetOf(seq1_array, seq2);
  }

  /*@ pure */ public static boolean subsetOf(Object[] seq1, Collection seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    Object[] seq2_array = seq2.toArray(new Object[]{});
    return subsetOf(seq1, seq2_array);
  }

  /**
   * Returns true iff seq contains no duplicate elements.
   */
  /*@ pure */ public static boolean noDups(Object[] seq) {
    if (seq == null) { return false; }
    return utilMDE.ArraysMDE.noDuplicates(seq);
  }

 /**
  * Returns true iff elt is in array arr.
  */
  /*@ pure */ public static boolean memberOf(Object elt, Object[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /**
   * Returns a subsequence of seq with first elements seq[start] and
   * last element seq[end].
   */
  /*@ pure */ public static Object[] slice(Object[] seq, int start, int end) {
    if (seq == null) { return null; }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) { return new Object[] { }; }
    if (end > seq.length-1) { return new Object[] { }; }
    if (sliceStart > sliceEnd) { return new Object[] { }; }
    int length = sliceEnd - sliceStart + 1;
    return utilMDE.ArraysMDE.subarray(seq, sliceStart, length);
  }

  /*@ pure */ public static Object[] slice(Object[] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  /*@ pure */ public static Object[] slice(Object[] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  /*@ pure */ public static Object[] slice(Object[] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  /*@ pure */ public static boolean eltsEqual(Object[] arr, Object elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  /*@ pure */ public static boolean eltsNotEqual(Object[] arr, Object elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  /*@ pure */ public static boolean pairwiseEqual(Object[] seq1, Object[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseEqual(java.util.AbstractCollection seq1, Object[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual((Object[])seq1.toArray(), seq2);
  }

  /*@ pure */ public static boolean pairwiseEqual(Object[] seq1, java.util.AbstractCollection seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, (Object[])seq2.toArray());
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseNotEqual(Object[] seq1, Object[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  /*@ pure */ public static boolean lexEqual(Object[] seq1, Object[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, seq2);
  }

  /**
   * Returns true iff seq1 is lexically not equal to seq2.
   */
  /*@ pure */ public static boolean lexNotEqual(Object[] seq1, Object[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return !lexEqual(seq1, seq2);
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseEqual(Object[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (ne(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseNotEqual(Object[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (eq(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /// Deferencing (accessing) fields

  /**
   * collectObject accepts an object and a list of fields (one of which is
   * of array type, and the rest of which are not), and produces an array in
   * which the original object has had the given fields accessed.
   *
   * Daikon creates invariants over "variables" such as the following.
   *
   * x.arr[].z   The result of collecting all elements y.z for all y's
   *             in array x.arr.
   * arr[].y.z   The result of collecting all elements x.y.z for all x's
   *             in array arr.
   * x.y.z[]     The result of collecting all elements in array x.y.z[]
   *
   * The collectObject() method does this collecting work.
   *
   * Given an object (x, arr, or x, correspondingly, in the above examples)
   * and a "field string" (arr.z, y.z, or y.z, correspondingly, in the
   * above example), the collect method collects the elements the result
   * from following the fields, one of which is assumed to be an array.
   *
   * requires: fieldStr.length() > 0 and object != null
   * requires: fieldStr contains only field names, no "[]" strings.
   *
   * requires: the method only works for field sequences with exactly
   * one field representing an array. For example, the collection
   * a[].b[].c will fail.
   *
   * If the resulting collection is of non-primitive type, then collect
   * returns an array of type Object[].
   *
   * Returns null if any array or field access causes an exception.
   */
  /*@ pure */ public static Object[] collectObject (Object object, String fieldStr) {

    if (object == null) { return null; }
    if (fieldStr == null) { return null; }

    //Assert.assertTrue(fieldStr != null && !"".equals(fieldStr));
    String[] fieldNames = fieldStr.split("\\.");
    Object[] retval = collectObject (object, fieldNames, 0);
    //System.err.println("%%% fieldArray returned: " + utilMDE.ArraysMDE.toString(retval));
    return retval;
  }

  /** Helper method for collectObject(Object, String).
   * Operates on the fields specified in fields[fieldsStartIdx..].
   * @see collectObject(Object, String)
   */
  /*@ pure */ private static Object[] collectObject (Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) { return null; }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj = null;
    try {
      Field field = (object instanceof java.lang.Class)
        ? ((Class)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      //System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {
        // last field is the collection
        return ((java.util.AbstractCollection)fieldObj).toArray(new Object[]{}); // unchecked
      } else

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (Object[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectObject_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        //Assert.assertTrue(cls.equals(_TYPE_WRAPPER_NAME.TYPE));
        return new Object[] { fieldObj };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection collection = (java.util.AbstractCollection)fieldObj;
        Object[] intermediate = new Object[collection.size()];
        int index = 0;
        for (Iterator i = collection.iterator() ; i.hasNext() ; ) {
          Object[] oneEltArray = collectObject (i.next(), fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        Object[] intermediate = new Object[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          Object[] oneEltArray = collectObject (Array.get(fieldObj, i),
                                             fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectObject (fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  // Returns the results of dereferencing the fields specified in
  // fields[fieldsStartIdx..] for 'object'.
  // Returns a default value if any field access causes an exception.
  /*@ pure */ public static Object collectObject_field (Object object, String fieldStr) {

    if (object == null) { return null; } // return default value
    if (fieldStr == null) { return null; } // return default value

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class)
          ? ((Class)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

      } catch (Exception e) {
        return null; // return default value

      }

    }

    return fieldObj;
  }

  ///////////////////////////////////////////////////////////////////////////
  /// Methods for "String" (from QuantBody.java.jpp)
  ///

  /** Returns the ith element of the array or collection argument.
   * If the argument is null or not an array or collection, returns a
   * default value (null).
   **/
  /*@ pure */ public static String getElement_String (Object o, long i) {
    if (o == null) { return null; } // return default value
    java.lang.Class c = o.getClass();
    if (c.isArray()) {
      return (String)java.lang.reflect.Array.get (o, (int)i);
    } else if (o instanceof java.util.AbstractCollection) {
      return (String)java.lang.reflect.Array.get (((java.util.AbstractCollection)o).toArray(), (int)i);
    } else {
      return null; // return default value
    }
  }

  /*@ pure */ public static String getElement_String (String[] arr, long i) {
    if (arr == null) { return null; } // return default value
    return arr[(int)i];
  }

  private static boolean eq(String x, String y) {
    return (x == y);
  }

  private static boolean ne(String x, String y) {
    return x != y;
  }

  /** True iff both sequences are non-null and have the same length. */
  /*@ pure */ public static boolean sameLength(String[] seq1, String[] seq2) {
    return ((seq1 != null)
            && (seq2 != null)
            && seq1.length == seq2.length);
  }

  /**
   * Returns the array { seq1[0], ..., seq1[seq1.length-1], seq2[0], ... , seq2[seq2.length-1] }
   * If either array is null, returns null.
   * If either array is empty, returns only those elements in the other array.
   * If both arrays are empty, returns a new empty array.
   */
  /*@ pure */ public static String[] concat(String[] seq1, String[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return utilMDE.ArraysMDE.concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set union of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static String[] union(String[] seq1, String[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return concat(seq1, seq2);
  }

  /**
   * Returns an array that is equivalent to the set intersection of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static String[] intersection(String[] seq1, String[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    String[] intermediate = new String[Math.min(seq1.length, seq2.length)];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (memberOf(seq1[i], seq2) ) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Returns an array that is equivalent to the set difference of seq1 and seq2.
   * This method gives no assurances about the order or repetition of elements:
   * elements may be repeated, and their order may be different from the
   * order of elements in seq1 and seq2.
   */
  /*@ pure */ public static String[] setDiff(String[] seq1, String[] seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    String[] intermediate = new String[seq1.length];
    int length = 0;
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        intermediate[length++] = seq1[i];
      }
    }
    return utilMDE.ArraysMDE.subarray(intermediate, 0, length);
  }

  /**
   * Retuns true iff seq1 and seq2 are equal when considered as sets.
   */
  /*@ pure */ public static boolean setEqual(String[] seq1, String[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2) ) {
        return false;
      }
    }
    for (int i = 0; i < seq2.length ; i++) {
      if (!memberOf(seq2[i], seq1) ) {
        return false;
      }
    }
    return true;
  }

  /** True iff seq1 is the reverse of seq2.
   *
   * Meaning (in pseudo-FOL):
   *
   * <pre>
   * /\ seq1.length == seq2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[seq2.length-1-i]
   * </pre>
   *
   */
  /*@ pure */ public static boolean isReverse(String[] seq1, String[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    int length = seq1.length;
    for (int i = 0 ; i < length ; i++) {
      if (ne(seq1[i], seq2[length - i - 1])) {
        return false;
      }
    }
    return true;
  }

  /**
   * True iff seq1 is a subset of seq2, when the sequences are
   * considered as sets.
   */
  /*@ pure */ public static boolean subsetOf(String[] seq1, String[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (!memberOf(seq1[i], seq2)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq contains no duplicate elements.
   */
  /*@ pure */ public static boolean noDups(String[] seq) {
    if (seq == null) { return false; }
    return utilMDE.ArraysMDE.noDuplicates(seq);
  }

 /**
  * Returns true iff elt is in array arr.
  */
  /*@ pure */ public static boolean memberOf(String elt, String[] arr) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return true; }
    }
    return false;
  }

  /**
   * Returns a subsequence of seq with first elements seq[start] and
   * last element seq[end].
   */
  /*@ pure */ public static String[] slice(String[] seq, int start, int end) {
    if (seq == null) { return null; }
    int sliceStart = start;
    int sliceEnd = end;
    if (start < 0) { return new String[] { }; }
    if (end > seq.length-1) { return new String[] { }; }
    if (sliceStart > sliceEnd) { return new String[] { }; }
    int length = sliceEnd - sliceStart + 1;
    return utilMDE.ArraysMDE.subarray(seq, sliceStart, length);
  }

  /*@ pure */ public static String[] slice(String[] seq, long start, int end) {
    return slice(seq, (int)start, end);
  }
  /*@ pure */ public static String[] slice(String[] seq, int start, long end) {
    return slice(seq, start, (int)end);
  }
  /*@ pure */ public static String[] slice(String[] seq, long start, long end) {
    return slice(seq, (int)start, (int)end);
  }

  /** True iff all elements in arr equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] == elt
   *
   */
  /*@ pure */ public static boolean eltsEqual(String[] arr, String elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (ne(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff every element in arr does not equal elt.
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..arr.length-1 } : arr[i] != elt
   *
   */
  /*@ pure */ public static boolean eltsNotEqual(String[] arr, String elt) {
    if (arr == null) { return false; }
    for (int i = 0 ; i < arr.length ; i++) {
      if (eq(arr[i], elt)) { return false; }
    }
    return true;
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] == seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] == seq2[i]
   *
   */

  /*@ pure */ public static boolean pairwiseEqual(String[] seq1, String[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (ne(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /*@ pure */ public static boolean pairwiseEqual(java.util.AbstractCollection seq1, String[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual((String[])seq1.toArray(), seq2);
  }

  /*@ pure */ public static boolean pairwiseEqual(String[] seq1, java.util.AbstractCollection seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, (String[])seq2.toArray());
  }

  /** True iff seq1 and seq2 have the same length, and every seq1[i] != seq2[i].
   *
   * Meaning (in pseudo-FOL):
   *
   * /\ seq1.length == se2.length
   * /\ forall i in { 0..seq1.length-1 } : seq1[i] != seq2[i]
   *
   */
  /*@ pure */ public static boolean pairwiseNotEqual(String[] seq1, String[] seq2) {
    if (! sameLength(seq1, seq2)) { return false; }
    for (int i = 0 ; i < seq1.length ; i++) {
      if (eq(seq1[i], seq2[i])) {
        return false;
      }
    }
    return true;
  }

  /**
   * Returns true iff seq1 is lexically equal to seq2.
   * For equality, "lexically" and "pairwise" are the same.
   */
  /*@ pure */ public static boolean lexEqual(String[] seq1, String[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(seq1, seq2);
  }

  /**
   * Returns true iff seq1 is lexically not equal to seq2.
   */
  /*@ pure */ public static boolean lexNotEqual(String[] seq1, String[] seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return !lexEqual(seq1, seq2);
  }

  /** True iff for all applicable i, every seq[i] == seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] == seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseEqual(String[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (ne(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /** True iff for all applicable i, every seq[i] != seq[i+1].
   *
   * Meaning (in pseudo-FOL):
   *
   * forall i in { 0..seq.length-2 } : seq[i] != seq[i+1]
   *
   */
  /*@ pure */ public static boolean eltwiseNotEqual(String[] seq) {
  if (seq == null) { return false; }
    for (int i = 0 ; i < seq.length ; i++) {
      if (i < seq.length-1) {
        if (eq(seq[i], seq[i+1])) {
          return false;
        }
      }
    }
    return true;
  }

  /// Deferencing (accessing) fields

  /**
   * collectString accepts an object and a list of fields (one of which is
   * of array type, and the rest of which are not), and produces an array in
   * which the original object has had the given fields accessed.
   *
   * Daikon creates invariants over "variables" such as the following.
   *
   * x.arr[].z   The result of collecting all elements y.z for all y's
   *             in array x.arr.
   * arr[].y.z   The result of collecting all elements x.y.z for all x's
   *             in array arr.
   * x.y.z[]     The result of collecting all elements in array x.y.z[]
   *
   * The collectString() method does this collecting work.
   *
   * Given an object (x, arr, or x, correspondingly, in the above examples)
   * and a "field string" (arr.z, y.z, or y.z, correspondingly, in the
   * above example), the collect method collects the elements the result
   * from following the fields, one of which is assumed to be an array.
   *
   * requires: fieldStr.length() > 0 and object != null
   * requires: fieldStr contains only field names, no "[]" strings.
   *
   * requires: the method only works for field sequences with exactly
   * one field representing an array. For example, the collection
   * a[].b[].c will fail.
   *
   * If the resulting collection is of non-primitive type, then collect
   * returns an array of type Object[].
   *
   * Returns null if any array or field access causes an exception.
   */
  /*@ pure */ public static String[] collectString (Object object, String fieldStr) {

    if (object == null) { return null; }
    if (fieldStr == null) { return null; }

    //Assert.assertTrue(fieldStr != null && !"".equals(fieldStr));
    String[] fieldNames = fieldStr.split("\\.");
    String[] retval = collectString (object, fieldNames, 0);
    //System.err.println("%%% fieldArray returned: " + utilMDE.ArraysMDE.toString(retval));
    return retval;
  }

  /** Helper method for collectString(Object, String).
   * Operates on the fields specified in fields[fieldsStartIdx..].
   * @see collectString(Object, String)
   */
  /*@ pure */ private static String[] collectString (Object object,
                                                   String[] fields, int fieldsStartIdx) {

    if (object == null) { return null; }
    assert (fields != null);
    assert (fieldsStartIdx >= 0 && fieldsStartIdx < fields.length);

    Object fieldObj = null;
    try {
      Field field = (object instanceof java.lang.Class)
        ? ((Class)object).getDeclaredField(fields[fieldsStartIdx])
        : object.getClass().getDeclaredField(fields[fieldsStartIdx]);
      field.setAccessible(true);
      // Class cls = field.getType();
      fieldObj = field.get(object);
      //System.out.println("***fieldObj="+fieldObj);

    } catch (Exception e) {
      return null;

    }

    if (fieldObj == null) {
      return null;
    }

    // base case: just accessed the last field
    if (fields.length - 1 == fieldsStartIdx) {

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {
        // last field is the collection
        return (String[]) (((java.util.AbstractCollection)fieldObj).toArray(new String[]{})); // unchecked
      } else

      if (fieldObj.getClass().isArray()) {
        // last field is an array
        return (String[])fieldObj;
      } else {
        // This hack should be removed in favor of, at "oneEltArray = ..."
        // below, calling a version of collectString_field that throws an
        // error.  Then, this case becomes a run-time error.  -MDE

        // Just one element; return a one-element array.
        //Assert.assertTrue(cls.equals(_TYPE_WRAPPER_NAME.TYPE));
        return new String[] { (String)fieldObj };
      }
    } else {
      // recursive case: more fields to access after this one

      if (daikon.ProglangType.list_implementors.contains(fieldObj.getClass().getName())) {

        java.util.AbstractCollection collection = (java.util.AbstractCollection)fieldObj;
        String[] intermediate = new String[collection.size()];
        int index = 0;
        for (Iterator i = collection.iterator() ; i.hasNext() ; ) {
          String[] oneEltArray = collectString (i.next(), fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[index++] = oneEltArray[0];
        }
        return intermediate;
      } else if (fieldObj.getClass().isArray()) {

        // collect elements across array
        String[] intermediate = new String[Array.getLength(fieldObj)];
        for (int i = 0 ; i < intermediate.length ; i++) {
          String[] oneEltArray = collectString (Array.get(fieldObj, i),
                                             fields, fieldsStartIdx + 1);
          //Assert.assertTrue(oneEltArray.length == 1);
          intermediate[i] = oneEltArray[0];
        }
        return intermediate;
      } else {

        return collectString (fieldObj, fields, fieldsStartIdx + 1);
      }
    }
  }

  // Returns the results of dereferencing the fields specified in
  // fields[fieldsStartIdx..] for 'object'.
  // Returns a default value if any field access causes an exception.
  /*@ pure */ public static String collectString_field (Object object, String fieldStr) {

    if (object == null) { return null; } // return default value
    if (fieldStr == null) { return null; } // return default value

    String[] fieldNames = fieldStr.split("\\.");

    // Holds the intermediate (and final) result
    Object fieldObj = object;

    for (int i = 0 ; i < fieldNames.length ; i++) {

      String fieldName = fieldNames[i];

      try {
        Field field =
          (fieldObj instanceof java.lang.Class)
          ? ((Class)fieldObj).getDeclaredField(fieldName)
          : fieldObj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        fieldObj = field.get(fieldObj);

      } catch (Exception e) {
        return null; // return default value

      }

    }

    return (String)fieldObj;
  }

  ///////////////////////////////////////////////////////////////////////////
  /// Collection methods (dispatch to array methods)
  //

  // These methods handle calls to quant methods that instead of passing an
  // Object[] array, pass a Collection.  Each method handles arrays or
  // Collections.  They convert any Collection to an array, then invoke the
  // Object[] version of the method.

  /** @see #noDups(Object[])
   */
  /*@ pure */ public static boolean noDups(Object seq) {
    if (seq == null) { return false; }
    return noDups(toObjArray(seq));
  }

  /** @see #typeArray(Object[])
   */
  /*@ pure */ public static String[] typeArray(Object seq) {
    if (seq == null) { return null; }
    return typeArray(toObjArray(seq));
  }

  /** @see #eltwiseEqual(Object[])
   */
  /*@ pure */ public static boolean eltwiseEqual(Object seq) {
    if (seq == null) { return false; }
    return eltwiseEqual(toObjArray(seq));
  }

  /** @see #eltwiseNotEqual(Object[])
   */
  /*@ pure */ public static boolean eltwiseNotEqual(Object seq) {
    if (seq == null) { return false; }
    return eltwiseNotEqual(toObjArray(seq));
  }

  /** @see #concat(Object[], Object[])
   */
  /*@ pure */ public static java.lang.Object[] concat(Object seq1, Object seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return concat(toObjArray(seq1), toObjArray(seq2));
  }

  /** @see #union(Object[], Object[])
   */
  /*@ pure */ public static java.lang.Object[] union(Object seq1, Object seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return union(toObjArray(seq1), toObjArray(seq2));
  }

  /** @see #intersection(Object[], Object[])
   */
  /*@ pure */ public static java.lang.Object[] intersection(Object seq1, Object seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return intersection(toObjArray(seq1), toObjArray(seq2));
  }

  /** @see #setDiff(Object[], Object[])
   */
  /*@ pure */ public static java.lang.Object[] setDiff(Object seq1, Object seq2) {
    if (seq1 == null) { return null; }
    if (seq2 == null) { return null; }
    return setDiff(toObjArray(seq1), toObjArray(seq2));
  }

  /** @see #setEqual(Object[], Object[])
   */
  /*@ pure */ public static boolean setEqual(Object seq1, Object seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return setEqual(toObjArray(seq1), toObjArray(seq2));
  }

  /** @see #isReverse(Object[], Object[])
   */
  /*@ pure */ public static boolean isReverse(Object seq1, Object seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return isReverse(toObjArray(seq1), toObjArray(seq2));
  }

  /** @see #pairwiseEqual(Object[], Object[])
   */
  /*@ pure */ public static boolean pairwiseEqual(Object seq1, Object seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseEqual(toObjArray(seq1), toObjArray(seq2));
  }

  /** @see #pairwiseNotEqual(Object[], Object[])
   */
  /*@ pure */ public static boolean pairwiseNotEqual(Object seq1, Object seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return pairwiseNotEqual(toObjArray(seq1), toObjArray(seq2));
  }

  /** @see #lexEqual(Object[], Object[])
   */
  /*@ pure */ public static boolean lexEqual(Object seq1, Object seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return lexEqual(toObjArray(seq1), toObjArray(seq2));
  }

  /** @see #lexNotEqual(Object[], Object[])
   */
  /*@ pure */ public static boolean lexNotEqual(Object seq1, Object seq2) {
    if (seq1 == null) { return false; }
    if (seq2 == null) { return false; }
    return lexNotEqual(toObjArray(seq1), toObjArray(seq2));
  }

  /** @see #memberOf(Object, Object[])
   */
  /*@ pure */ public static boolean memberOf(java.lang.Object elt, Object arr) {
    if (arr == null) { return false; }
    return memberOf(elt, toObjArray(arr));
  }

  /** @see #slice(Object[], int, int)
   */
  /*@ pure */ public static java.lang.Object[] slice(Object seq, int start, int end) {
    if (seq == null) { return null; }
    return slice(toObjArray(seq), start, end);
  }

  /** @see #eltsEqual(Object[], Object)
   */
  /*@ pure */ public static boolean eltsEqual(Object arr, java.lang.Object elt) {
    if (arr == null) { return false; }
    return eltsEqual(toObjArray(arr), elt);
  }

  /** @see #eltsNotEqual(Object[], Object)
   */
  /*@ pure */ public static boolean eltsNotEqual(Object arr, java.lang.Object elt) {
    if (arr == null) { return false; }
    return eltsNotEqual(toObjArray(arr), elt);
  }

  /*@ pure */ public static boolean isIntegralType(Class c) {
    if (c == null) { return false; }
    return
      (c.equals(Byte.TYPE) ||
       c.equals(Short.TYPE) ||
       c.equals(Integer.TYPE) ||
       c.equals(Long.TYPE));
  }

  /*@ pure */ public static boolean isRealType(Class c) {
    if (c == null) { return false; }
    return
      (c.equals(Float.TYPE) ||
       c.equals(Double.TYPE));
  }

  /*@ pure */ public static boolean isNumericType(Class c) {
    if (c == null) { return false; }
    return isIntegralType(c) || isRealType(c);
  }

  /** Returns an Object[] array, either by casting or by conversion from an
   * AbstractCollection.
   */
  public static Object[] toObjArray(Object o) {
    if (o == null) { return null; }
    if (o instanceof java.util.AbstractCollection) {
      return ((AbstractCollection)o).toArray(new java.lang.Object[]{}); // unchecked cast
    } else if (o.getClass().isArray()) {
      return (Object[])o;
    } else {
      return null;
    }
  }

}
